1

我有一个讨厌的问题,我真的无法理解。

基本上我有一个定义如下的 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 和 getter 中,我们解析/生成包含数据库 XML 的字符串。我知道这是一个丑陋的把戏,但它有效并完成了工作。

4

1 回答 1

1

基于这个对一个非常相似的问题的回答,问题的原因似乎是 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 的链接答案,因为它太棒了。)

于 2012-09-21T14:30:55.920 回答