2012-09-21 17 views
1

我有一個令人討厭的問題,我真的無法理解。如果DbContext被封裝在一個接口中,LINQ查詢無法被翻譯

基本上我有定義的的DbContext如下

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 
} 

由於我在一些測試揹着,我開始在這方面的工作,而無需關心的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')。

這怎麼可能?微軟用反射玩弄了一些骯髒的伎倆嗎

編輯: 兩個XmlValue和XmlTypeDescriptor有以下幾種接口的實現,我們用它來自動讀取XML字段的內容轉換成合適的對象。

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

基本上我們將字段的內容映射到RawData屬性;在其setter和getters中,我們解析/生成包含數據庫XML的字符串。 我知道這是一個醜陋的伎倆,但它工作並完成工作。

回答

1

基於this answer一個非常類似的問題,該問題的原因似乎是EF不理解您的自定義接口類型,可查詢表達式的性質,並認爲context.Properties集合作爲SettingsProperty類型的常量對象的集合你想傳入查詢。 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,但我不知道。

(如果上述作品別忘了給予好評Eranga的鏈接的答案,因爲它的真棒)

+0

哎喲,我無法找到這個問題:S – Kralizek