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プロパティにマップします。セッターとゲッターでは、データベースの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、私にはわかりません。

(上記の作品がうまくいったら、それは素晴らしいので、エランガのリンクされた答えに賛成することを忘れないでください。)

于 2012-09-21T14:30:55.920 に答える