0

我有两个类要存储在 EF Code First 中。建筑物有一个维护者,以及在那里工作的人员列表。维护人员不必在大楼内工作。

在我第一次尝试时,我刚刚

public class Person
    {
        public virtual Guid Id { get; set; }
        public virtual string Name { get; set; }

    }

public class Building
{
    public virtual  Guid Id { get; set; }
    public virtual List<Person> WorksHere { get; set; }

    public virtual Person MaintainedBy { get; set; }
    public virtual String Address { get; set; }
}

这给了我两个带有基本属性的表,以及关于 People 的 Building_Id 和关于 Buildings 的 MaintainedBy_Id。

当我运行测试程序时

        using (TestContext tc = new TestContext())
        {
            Person m1 = tc.persons.Create();
            m1.Name = "maintainB1";
            m1.Id = Guid.NewGuid();

            Person m2 = tc.persons.Create();
            m2.Name = "maintainB2";
            m2.Id = Guid.NewGuid();

            Building b1 = tc.buildings.Create();
            b1.Address = "building1";
            b1.Id = Guid.NewGuid();
            tc.buildings.Add(b1);

            Building b2 = tc.buildings.Create();
            b2.Address = "building1";
            b2.Id = Guid.NewGuid();
            tc.buildings.Add(b2);

            b1.MaintainedBy = m1;
            b2.MaintainedBy = m2;

            if (b1.WorksHere == null) b1.WorksHere = new List<Person>();
            if (b2.WorksHere == null) b2.WorksHere = new List<Person>();


            b1.WorksHere.AddRange(new List<String>() { "e11", "e12", "e13" }.Select(s =>
            {
                Person p = new Person();
                p.Id = Guid.NewGuid();
                p.Name = s;
                return p;
            }));

            b2.WorksHere.AddRange(new List<String>() { "e21", "e22", "e23" }.Select(s =>
            {
                Person p = new Person();
                p.Id = Guid.NewGuid();
                p.Name = s;
                return p;
            }));


            b1.WorksHere.Add(m2);
            b2.WorksHere.Add(m1);
            tc.SaveChanges();
        }

    }

我收到一个异常:“保存不为其关系公开外键属性的实体时发生错误。EntityEntries 属性将返回 null,因为无法将单个实体标识为异常源。保存时处理异常可以是通过在实体类型中公开外键属性变得更容易。有关详细信息,请参阅 InnerException。

带有内部异常:“无法确定依赖操作的有效排序。由于外键约束、模型要求或存储生成的值,可能存在依赖关系。”

我不想在我的 Poco 上公开更多属性,因为它们是 Poco 的,我对这些属性没有用处。如果我必须满足 Code First 模型生成,那就必须这样做,但如果可能的话,我希望在映射类中远离我的 Poco。

我该如何解决?

4

2 回答 2

2

因此,出现此问题是因为您的实体之间存在循环关系,这导致 EF 在尝试在一次调用中解决所有插入时放弃SaveChanges,并引发您看到的异常。

为了理解为什么它不能处理这种情况,让我们想想当试图保存实体时数据库中发生了什么。

使用您的代码,您可以通过在调用之前注释掉最后一行来使其运行没有错误SaveChanges,但是人员m1将不会在 building 中工作b2,所以这不是您想要的。

b1.WorksHere.Add(m2);
//b2.WorksHere.Add(m1); <-- When this is removed it works.. 
tc.SaveChanges();

但是,EF 可以通过在数据库中创建以下插入来运行它:

  1. 插入人m1。将建筑物表的 FK 保留为空,因为m1无处可工作。
  2. 插入建筑物'b1'。使用 'm1' 的 id 作为 FK,因为m1维护b1.
  3. 插入人m2。使用 id ofb1作为 FK,因为b1它是m2有效的。
  4. 插入建筑物b2。使用 idm2作为 FK,因为m2维护b2.

现在很容易看出为什么当你包含使m1工作的行时它不起作用b2
在上面的第一个插入中,EF 无法将 FK 保留为空,因为您告诉它它需要指向一个建筑物,但该建筑物尚未插入,因此它无法创建 FK 指向回到它。
当实体具有循环依赖关系时,这在 EF 中始终是一个问题。当两者相互依赖时,不能在一次提交中创建插入。

您的问题的解决方案是简单地调用两次SaveChanges. 如果你在m1工作之前调用它b2,然后再调用它,你会得到正确的行为。

b1.WorksHere.Add(m2);
tc.SaveChanges(); <-- Create inserts. FK in m1 is null because he works nowhere yet.
b2.WorksHere.Add(m1);
tc.SaveChanges(); <-- Updates FK in m1 to point to b2.

实体框架中的未来支持
似乎这个问题将在 EF 的未来版本中得到解决。

可以合理地期望 ORM 应该能够处理插入父级、插入子级,然后使用新创建的子级 ID 更新父级。

您可以阅读有关它的更多信息并投票支持要在Microsoft Connect上实施的功能。

EF CodePlex 网站上也有一些信息。

于 2013-03-27T23:14:18.177 回答
0

尝试映射关系。在从 DbContext 派生的上下文中,覆盖 OnModelCreating 方法:

protected override void OnModelCreating(DbModelBuilder mb)
{
    mb.Entity<Building>()
        .HasMany(m => m.LivesHere)
        .WithRequired()
        .Map(n => n.MapKey("Home_Id"));
}

我也会尝试使用 [Key] 来表示实体中的主键,但我不确定你是否必须这样做。

像这样:

public class Building
{
    [Key]
    public virtual  Guid Id { get; set; }
    public virtual List<Person> WorksHere { get; set; }

    public virtual Person MaintainedBy { get; set; }
    public virtual String Address { get; set; }
}

我现在不能尝试这个,但我希望它会有所帮助。

于 2013-03-27T14:25:10.257 回答