【问题标题】:Changing foreign key has no effect in EF4 code first首先更改外键在 EF4 代码中无效
【发布时间】:2012-04-22 15:38:20
【问题描述】:

我有一个使用 Entity Framework Code First 定义的实体,它有一个可选的父节点字段。空父节点值意味着实体的节点位于根节点。同一父母下会有多个孩子。

Class ContentDescriptor
{
    virtual ObjectId ParentObjectId{get;set;}
}

在数据库初始化器中:

HasOptional(contentDescriptor => contentDescriptor.ParentObjectId).WithOptionalDependent()
    .Map(m => m.MapKey("ParentObjectId"));

导航属性分配如下:

  DBContext.ContentDescriptors.Add(contentDescriptor);
  contentDescriptor.ParentObjectId = ObjectIdFactory.Save(parent);

ObjectIdFactory.Save 检查 DBContext.ObjectId.Local 中是否有条目,然后检查 DBContext.ObjectId,如果内存或 DB 中没有记录,则插入并返回结果。

但是,当新节点被添加到上下文中时,只有父节点的第一个子节点被正确保存。第二个和以后的子级被添加到根,即使它们在本地显示为父级的子级。看起来框架在添加实体时无法为外键生成更新查询。

我的解决方法是在实体中公开外键并手动分配外键,但我想知道是否有更优雅的解决方案:

HasOptional(contentDescriptor => contentDescriptor.ParentObjectId).WithMany().
HasForeignKey(c=>c.ParentObjectIdRaw);
Property(c => c.ParentObjectIdRaw).HasColumnName("ParentObjectId");


contentDescriptor.ParentObjectIdRaw = contentDescriptor.ParentObjectId.ObjectIdId;

【问题讨论】:

    标签: entity-framework-4.1 ef-code-first


    【解决方案1】:

    这通常最适合我(注意:我使用的是 4.3,我认为它应该不会有太大不同,但不确定)...
    (我使用伪 Node 作为自引用实体,我认为其他一切都一样)

    public class Node
    {
        public int NodeID { get; set; }
        public string Name { get; set; }
        public virtual Node Parent { get; set; }
        public virtual ICollection<Node> Children { get; set; }
    }
    

    ...

    modelBuilder.Entity<Node>()
        .HasOptional(n => n.Parent)
        .WithMany(n => n.Children);
    

    ...即您的实现问题可能是WithOptionalDependent,它通常用于一对一的关系(正如您所说,您有一对多)。

    在使用方面...

    using (var db = new UserDbContext())
    {
        var root = new Node { Name = "root" };
        var node = new Node { Name = "first", Parent = root };
        var child1 = new Node { Name = "child1", Parent = node };
        var child2 = new Node { Name = "child2", Parent = node };
        db.Nodes.Add(child1);
        db.Nodes.Add(child2);
        db.SaveChanges();
        foreach (var n in db.Nodes.Include(x => x.Parent))
            Console.WriteLine("node: {0}, {1}, {2}, {3}, {4}", n.Name, n.NodeID, n.Parent != null ? n.Parent.Name : "", n.Parent != null ? n.Parent.NodeID : 0, n.Children != null ? n.Children.Count : 0);
    }
    

    ...据我所知,您应该只添加子项(自动添加父项)。或者使用Children 反过来添加,所以将子节点添加到父节点中,在这种情况下只保存根就足够了。

    不确定这是否能解决你所拥有的问题(我不确定你在后面用 ObjectIdFactory.Save 做什么)但它是相同的模式,你不必真的去拯救父母,这就足够了在SaveChanges() 之前只救一个孩子

    希望对大家有帮助

    【讨论】:

    • 基本上每个其他实体都有一个 ObjectId,我不想在与 ObjectIds 相关的查询中引入不必要的联接,因此父对象 id 上没有集合属性。我尝试了 WithMany 并设置了导航属性,但没有效果。
    • collection 属性不会“添加”任何东西,只是为您提供额外的导航属性(这是现有关系的另一部分)。无论如何,您可以使用 '.WithMany()' 并删除集合,它不会改变任何东西。在我这边,所有节点都被正确添加等等。确保你没有在该方法中做任何非典型的事情
    • 我的实体关系有点不同。只有一些对象类型会有树结构,其他对象可能有一个平面列表或其他结构。具有树结构的对象类型将它们的关系保存在 ContentDescriptor 表中。我从合作伙伴那里查找的所有信息都是基于 ObjectIds,因此 ParentObjectId 从缓存列变为定义父子关系的唯一列。添加 ObjectId.ParentObjectId 列会浪费大量存储空间,并且 ContentDescriptor.ParentContentDescriptor 搜索效率低。
    猜你喜欢
    • 1970-01-01
    • 2011-05-15
    • 2023-04-05
    • 1970-01-01
    • 2012-01-08
    • 1970-01-01
    • 1970-01-01
    • 2014-10-04
    • 2023-04-04
    相关资源
    最近更新 更多