2012-09-21 3 views
1

정말 이해할 수없는 불쾌한 문제가 있습니다. 나는 몇 가지 테스트를 수행 된 이후DbContext가 인터페이스에 래핑 된 경우 LINQ 쿼리를 번역 할 수 없습니다.

public interface ISettingsContext : IDisposable 
{ 
    IDbSet<Site> Sites { get; } 
    IDbSet<SettingGroup> Groups { get; } 
    IDbSet<SettingProperty> Properties { get; } 

    int SaveChanges(); 
} 

public class SettingsContext : DbContext, ISettingsContext 
{ 
    public SettingsContext(string connectionStringName) : base(connectionStringName) 
    { 
     Sites = Set<Site>(); 
     Groups = Set<SettingGroup>(); 
     Properties = Set<SettingProperty>(); 
    } 

    public SettingsContext() : this("DefaultTestConnectionString") { } 

    public IDbSet<Site> Sites { get; private set; } 

    public IDbSet<SettingGroup> Groups { get; private set; } 

    public IDbSet<SettingProperty> Properties { get; private set; } 

    #region Model Creation 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     MapSite(modelBuilder.Entity<Site>()); 
     MapGroup(modelBuilder.Entity<SettingGroup>()); 
     MapProperty(modelBuilder.Entity<SettingProperty>()); 
     MapValue(modelBuilder.Entity<SiteSettingValue>()); 
     MapXmlValue(modelBuilder.ComplexType<XmlValue>()); 
     MapXmlType(modelBuilder.ComplexType<XmlTypeDescriptor>()); 
    } 

    private void MapXmlType(ComplexTypeConfiguration<XmlTypeDescriptor> complexType) 
    { 
     complexType.Ignore(t => t.Type); 
    } 

    private void MapXmlValue(ComplexTypeConfiguration<XmlValue> complexType) 
    { 
     complexType.Ignore(t => t.Value); 
    } 

    private void MapValue(EntityTypeConfiguration<SiteSettingValue> entity) 
    { 
     entity.HasKey(k => new { k.PropertyId, k.SiteId }).ToTable("SiteValues", "settings"); 

     entity.Property(k => k.PropertyId).HasColumnName("FKPropertyID"); 

     entity.Property(k => k.SiteId).HasColumnName("FKSiteID"); 

     entity.Property(k => k.Value.RawData).HasColumnName("Value").IsMaxLength(); 

     entity.HasRequired(k => k.Site).WithMany(s => s.Settings).HasForeignKey(k => k.SiteId); 

     entity.HasRequired(k => k.Property).WithMany().HasForeignKey(k => k.PropertyId); 
    } 

    private void MapProperty(EntityTypeConfiguration<SettingProperty> entity) 
    { 
     entity.HasKey(k => k.Id).ToTable("Properties", "settings"); 

     entity.Property(k => k.Id); 

     entity.Property(k => k.Name); 

     entity.Property(k => k.GroupId).HasColumnName("FKGroupID"); 

     entity.Property(k => k.PropertyDescriptor.RawData).HasColumnName("TypeDescriptor").IsMaxLength(); 

     entity.Property(k => k.DefaultValue.RawData).HasColumnName("DefaultValue").IsMaxLength(); 

     entity.HasRequired(k => k.Group).WithMany(k => k.Properties).HasForeignKey(k => k.GroupId); 
    } 

    private void MapGroup(EntityTypeConfiguration<SettingGroup> entity) 
    { 
     entity.HasKey(k => k.Id).ToTable("Groups", "settings"); 

     entity.Property(k => k.Id); 

     entity.Property(k => k.Name); 

     entity.HasMany(k => k.Properties).WithRequired(k => k.Group).HasForeignKey(k => k.GroupId); 
    } 

    private void MapSite(EntityTypeConfiguration<Site> entity) 
    { 
     entity.HasKey(k => k.Id).ToTable("Sites", "site"); 

     entity.Property(k => k.Id); 

     entity.Property(k => k.Domain); 
    } 

    #endregion 
} 

을 다음과 같이

은 기본적으로 내가 정의 DbContext을 가지고, 나는 IOC의와 DI에 대한 배려없이이 상황에 맞는 작업을 시작, 그래서 구체적인 클래스 자체에 대해 직접했다.

그래서 나는이 쿼리 작성 :

public class SiteSettingsLoader : ILoader<SiteSettingsModel, int> { 
... 
var qSiteValues = from site in context.Sites 
        let settings = site.Settings 
        select new 
        { 
          SiteId = site.Id, 
          Settings = from value in settings 
            join property in context.Properties on value.PropertyId equals property.Id into props 
            from property in props 
            select new 
            { 
              PropertyId = property.Id, 
              Name = property.Name, 
              GroupId = property.GroupId, 
              Value = value.Value, 
              CanBeInherited = property.CanBeInherited 
            } 
        }; 

을 그리고 모든 것이 잘 작동했다. 컨텍스트를 실제 유형 대신 구현하는 인터페이스로 컨텍스트를 전달하기 시작할 때 문제가 발생했습니다.

using (ISettingsContext context = new SettingsContext()) 
using (SiteSettingsLoader loader = new SiteSettingsLoader(context)) 
{ 
    SiteSettingsStore store = new SiteSettingsStore(loader); 
    dynamic settings = store.GetItem(1); 
} 

오류 유형 의 상수 값을 생성 할 수 없습니다

인 'Settings.Entities.SettingProperty'. 이 문맥에서는 원시 타입 (' Int32, String, Guid'등) 만 지원됩니다.

어떻게 가능합니까? Microsoft는 리플렉션을 통해 더러운 속임수를 사용 했습니까?

편집 : XmlValue와 XmlTypeDescriptor 모두 우리가 자동으로 적절한 개체로는 XML 필드의 내용을 읽는 데 사용할 다음과 같은 인터페이스의 구현입니다.

public interface IXmlProperty 
{ 
    string RawData { get; set; } 
    void Load(string xml); 
    XDocument ToXml(); 
} 

기본적으로 우리는 필드의 내용을 RawData 속성에 매핑합니다. setter와 getter에서 우리는 데이터베이스 용 XML을 포함하는 문자열을 구문 분석/생성합니다. 나는 그것이 추한 속임수라는 것을 알고 있지만 일을 마치고 일을 끝낸다.

답변

1

문제에 대한 이유는 EF는 쿼리 가능한 표현으로 사용자 정의 인터페이스 유형의 특성을 이해하지 못하는 것 같다 입력 SettingsProperty의 일정 개체의 컬렉션으로 context.Properties 수집을 고려 매우 유사한 질문에 this answer을 바탕으로 그 당신은 쿼리에 전달하고 싶습니다. 예외를 야기하는 EF에서는 기본이 아닌 상수 오브젝트를 사용하는 것을 지원하지 않습니다.

기본적으로 EF가 전체 쿼리에 통합하기 위해 SQL 조각으로 변환하는 방법을 알고있는 (컴파일 시간) 유형을 사용해야합니다. 즉, DbContext.DbSet<T> 자체 또는 IQueryable<T> 것 :

IQueryable<SettingProperty> properties = context.Properties; 
var qSiteValues = from site in context.Sites 
        let settings = site.Settings 
        select new 
        { 
         SiteId = site.Id, 
         Settings = from value in settings 
           join property in properties 
            on value.PropertyId equals property.Id 
            into props 
           from property in props 
           select new 
           { 
            PropertyId = property.Id, 
            Name = property.Name, 
            GroupId = property.GroupId, 
            Value = value.Value, 
            CanBeInherited = property.CanBeInherited 
           } 
       }; 

context.Sites에 같은 트릭을 사용하는 데 필요한 수 있습니다하지만 난 확실하지 않다.

+0

아야를 (. 위의 작품이 최고이기 때문에 Eranga의 링크 대답을 upvote에 잊지하지 않는 경우)

, 그 질문을 찾을 수 없습니다 : S를 – Kralizek