为什么实体框架没有将我的集合加载到基类中?

时间:2014-10-22 17:53:41

标签: c# entity-framework dynamic entity-framework-6

我有一个继承自DynamicObject的基础对象,我将其用作实体POCO的基类,这允许POCO像ExpandoObject一样工作({{1}像引擎盖下的对象)我将该字典映射到edm兼容的Key / Value类,如下所示:

Dictionary<string,object>

并为我们需要存储的每种系统类型提供强类型继承类,即

public class EntityPair<TKey, TValue>
{
   [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
   public int Id { get; set; }
   public TKey Key { get; set; }
   public TValue Value { get; set; }
   public string TableName { get; set; }
   public int FK { get; set; }
}

并且基本expando有一个公共属性,它将获取类型的所有动态属性(如字符串),因为它是相应的键值类型。例如,对于字符串属性,它将返回public class EntityPairString: EntityPair<string,string> { } 个对象的集合。

现在我遇到的问题是它没有加载数据库的类型集合(如字符串集合)退出 - 我可以存储它。但是当我检索它时,我无法获得字符串KVPair集合。有什么奇怪的是EF说它知道关系,并加载了集合,但它没有设置集合 - 即使我急切地或明确地加载它。

实体:

EntityPairString

它继承的Expando:

public class Order : Expando
{
    public Order()
    {
        TableName = "Order";
    }

    public short OrderType { get; set; }
    public int InitiatorId { get; set; }
    public string InitiatorName { get; set; }

    [...]

    public class OrderConfiguration : EntityTypeConfiguration<Order>
    {
        public OrderConfiguration()
        {
            Map(c => c.MapInheritedProperties());
            HasMany(c => c.StringExpando)
                     .WithOptional()
                     .HasForeignKey(c => c.FK)
                     .WillCascadeOnDelete(true);
        }
    }
}

其他一切正常,就像我说的那样,我可以很好地为数据库添加值,没有发生的事情是从数据库中加载属性。 [Serializable] public class Expando : DynamicObject, IDynamicMetaObjectProvider { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } protected string TableName { get; set; } [...] public virtual ICollection<EntityPairString> StringExpando { get { var values = ((IEnumerable<EntityPair<string, object>>)Properties) .Where(c => c.Value is string) .Select(d => new EntityPairString() { Key = d.Key, Value = d.Value as string, Id = d.Id, FK = Id, TableName = TableName }).ToList(); return values; } set { if (Properties == null) Properties = new PropertyBag(); foreach (var i in value) { var p = (EntityPair<string, string>)i; Properties.Add(new EntityPair<string, object> { Id = i.Id, Key = i.Key, Value = i.Value, FK = Id, TableName = TableName }); } } } } 对象返回正常,但它不会加载字符串expando属性。

注意:虽然Order中的实体映射表明它是外键,但我从显式数据库迁移中删除了它。该关系已编入索引,并在运行时期间仍在EF的内部列表中列出。我通过致电:

验证了这一点
Order.OrderConfiguration

所以简单来说,我的问题是,当我加载任何或所有var relationshipManager = ((IObjectContextAdapter) ctx).ObjectContext .ObjectStateManager.GetRelationshipManager(order); var relations = relationshipManager.GetAllRelatedEnds(); 个实体时,为什么我的EntityPairString集合没有被EF附加到Order对象?

彻底性:

显式迁移的相关部分:

Order

更新

就在我的调试过程中,我决定确认它正在生成正确的查询,所以我做了一个SQL配置文件,它运行的查询是:

CreateTable(
    "dbo.expando_string",
     c => new
     {
         Id = c.Int(nullable: false, identity: true),
         TableName = c.String(nullable: false, maxLength: 128),
         FK = c.Int(nullable: false),
         Key = c.String(),
         Value = c.String(),
     })
     .PrimaryKey(t => new { t.Id, t.TableName, t.FK })
     .Index(t => t.FK);

在执行时会返回正确的结果集,所以在EF C#代码中的某个地方它只是没有附加实体。

1 个答案:

答案 0 :(得分:0)

我弄清楚出了什么问题--EF没有设置集合,它只添加或删除它们,如果getter返回null,它将初始化一个集合,但是它仍然会添加到刚刚初始化的集合中(这就是为什么你可以在懒惰加载其余的集合之前添加一个项目)

所以问题在于:

public virtual ICollection<EntityPairString> StringExpando
{
    get
    {
        var values = ((IEnumerable<EntityPair<string, object>>)Properties)
            .Where(c => c.Value is string)
            .Select(d => new EntityPairString()
            {
                Key = d.Key,
                Value = d.Value as string,
                Id = d.Id,
                FK = Id,
                TableName = TableName
            }).ToList();
        return values;
    }
    set
    {
        if (Properties == null) Properties = new PropertyBag();
        foreach (var i in value)
        {
            var p = (EntityPair<string, string>)i;
            Properties.Add(new EntityPair<string, object>
            {
                Id = i.Id,
                Key = i.Key,
                Value = i.Value,
                FK = Id,
                TableName = TableName
            });
        }
    }
}

我必须进行一些重组,但我最终通过实际返回集合来实现它,所以当它添加到集合中时我会意识到它。

简单地说,这是我的一个愚蠢的错误,EF实际上已经为我设置了值,但是它将它们设置在我无法处理的列表中。

EF用于保存和获取集合的最终方法是什么:

    public ICollection<ExpandoString> StringExpando
    {
        get
        {
            // ReSharper disable ForCanBeConvertedToForeach
            for (var i = 0; i < _strings.Count; i++)
            // ReSharper restore ForCanBeConvertedToForeach
            {
                _strings[i].FK = Id;
                _strings[i].TableName = TableName;
            }
            //using for as we don't want to enumerate the bag
            return _strings.List;
        }
    }

使用可扩展对象作为实体的唯一警告是,您必须明确加载可扩展实体上的所有关系,但是,它们将很轻,因此它不应该是一个交易破坏者。