Entity framework 多重链接的1-1关系不';按预期同步PKs

Entity framework 多重链接的1-1关系不';按预期同步PKs,entity-framework,entity-framework-4,primary-key,foreign-key-relationship,cascade,Entity Framework,Entity Framework 4,Primary Key,Foreign Key Relationship,Cascade,我尝试搜索SO,但我找到的所有结果似乎都涉及更新已持久化到DB的实体的PK。我的情况不同 我在一个数据库中有3个表,具有1-0..1关系。这些关系如下所示: A <- B <- C 现在是链接和保存代码: // a is an instance of A that has been loaded from DB // and hence has a persistent Id value. // b is a just-created instance of B // that h

我尝试搜索SO,但我找到的所有结果似乎都涉及更新已持久化到DB的实体的PK。我的情况不同

我在一个数据库中有3个表,具有1-0..1关系。这些关系如下所示:

A <- B <- C
现在是链接和保存代码:

// a is an instance of A that has been loaded from DB
// and hence has a persistent Id value.
// b is a just-created instance of B
// that has a non-persistent Id value and null reference to A.
void SaveBlahBlahBlah(A a, B b)
{
  // At this point b and c have the same Id value.
  // It differs from a's Id, but that's expected, they haven't been linked yet.
  b.A = a;
  // At this point b receives a's Id value, but c keeps the original one,
  // therefore the existing b-c link gets broken!

  using(var ctx = new MyContext())
  {
    ctx.As.Attach(a); // This throws exception saying
    // I've violated referential integrity.
    // It doesn't say which relationship is broken,
    // but I guess it's the B-C one since
    // the debugger shows them to have different values if PKs

    ctx.Bs.AddObject(b);

    ctx.SaveChanges();
  }
}
我已经用默认EF的代码生成器(使用EF的
实体
类作为生成实体的基类)和自跟踪实体代码生成器尝试了这两种方法。结果是一样的

所以,代码崩溃了。原因可能是在链接了
A
B
之后,
B
C
获得不同的PK值,这对于具有1-1关系的实体是非法的

我所期望的是C自动将其PK同步到从
A
实例获得的
B
值。这似乎是合理的,因为我使用的是一个对象图,我有一个现有的
B
-
C
关系,它是正常的,我希望它在链接
B
A
后保持正常。为什么会破裂?如果数据库中存在
B
C
,并且我无法更改他们的PKs,我会理解的。但事实并非如此,两个实体都是刚刚创建的

我无法通过为FKs使用独立于PKs列来断开键链,因为EF要求1-1关系的两侧都是PKs

我不想手动同步键,因为实际上有更多1-1相关的表,这需要同步代码出现在许多地方

我相信我将能够更新STE生成器的T4模板,以级联PK更新1-1关系。但我对T4不太熟悉,也不太乐意这么做

我有两个问题:

  • 出于某些原因,我对级联PK更新的期望是错误的吗?(看起来很奇怪)也就是说,这是一个bug还是一个特性
  • 与修改STE模板相比,是否有其他更简单的方法来解决此问题?也许在EF映射或上下文中有一些神奇的选项

  • 提前感谢。

    问题在于,处理从一个引用对象到另一个引用对象的ID分配的服务是上下文。但当您实际进行关联时,两个对象都不在上下文中。这通常不会是一个问题,因为当您将B添加到上下文中时,关系将得到修复

    不幸的是,你没有这样做。相反,您创建了一个与的附加关系,但随后对上下文撒谎并声称一切都已修复。更准确地说,您可以调用
    EntitySet.Attach
    ,它实际上只用于已经固定的对象

    另一方面,这样的代码应该可以正常工作:

    public B CreateB()
    {
      return new B
        {
          Id = Guid.NewGuid(),
          C = new C(),
        };
    }
    
    void SaveBlahBlahBlah(A a, B b)
    {    
      using(var ctx = new MyContext())
      {    
        ctx.Bs.AddObject(b);
    
        ctx.SaveChanges();
      }
    }
    
    注意,我在这里所做的只是删除有问题的代码,这与B和C之间的关系无关

    简言之,注意
    附加
    。你需要知道你打电话时在做什么

    更新

    处理
    A
    的现有实例的版本:

    void SaveBlahBlahBlah(A a, B b)
    {    
      Debug.Assert(a.B != b);
    
      using(var ctx = new MyContext())
      {    
        ctx.As.Attach(a);
    
        a.B = b; // it's crucial that this link is set after attaching a to context!
    
        ctx.Bs.AddObject(b);
    
        ctx.SaveChanges();
      }
    }
    

    问题在于,处理从一个引用对象到另一个引用对象的ID分配的服务是上下文。但当您实际进行关联时,两个对象都不在上下文中。这通常不会是一个问题,因为当您将B添加到上下文中时,关系将得到修复

    不幸的是,你没有这样做。相反,您创建了一个与的附加关系,但随后对上下文撒谎并声称一切都已修复。更准确地说,您可以调用
    EntitySet.Attach
    ,它实际上只用于已经固定的对象

    另一方面,这样的代码应该可以正常工作:

    public B CreateB()
    {
      return new B
        {
          Id = Guid.NewGuid(),
          C = new C(),
        };
    }
    
    void SaveBlahBlahBlah(A a, B b)
    {    
      using(var ctx = new MyContext())
      {    
        ctx.Bs.AddObject(b);
    
        ctx.SaveChanges();
      }
    }
    
    注意,我在这里所做的只是删除有问题的代码,这与B和C之间的关系无关

    简言之,注意
    附加
    。你需要知道你打电话时在做什么

    更新

    处理
    A
    的现有实例的版本:

    void SaveBlahBlahBlah(A a, B b)
    {    
      Debug.Assert(a.B != b);
    
      using(var ctx = new MyContext())
      {    
        ctx.As.Attach(a);
    
        a.B = b; // it's crucial that this link is set after attaching a to context!
    
        ctx.Bs.AddObject(b);
    
        ctx.SaveChanges();
      }
    }
    

    谢谢我应该注意,我使用了
    Attach
    ,因为没有它,EF试图插入
    A
    ,这导致了一个错误,因为
    A
    已经存在于数据库中。从那时起,我改变了模式和口味(我当时使用的是EFCF)。因此,也许你的建议会奏效,我会尝试一下。你可以使用
    附件
    ,但在你与B建立关系之前,你需要这样做。好的,它奏效了。我已经编辑了你的答案,以包含我需要的确切代码。然而,我还是不太了解情况。对于依赖于上下文和类来跟踪更改和状态的普通EF来说,这似乎还可以。但是为什么它对STE不起作用呢?他们应该在没有现有背景的情况下跟踪变化,这正是自我跟踪背后的理念,不是吗?谢谢。我应该注意,我使用了
    Attach
    ,因为没有它,EF试图插入
    A
    ,这导致了一个错误,因为
    A
    已经存在于数据库中。从那时起,我改变了模式和口味(我当时使用的是EFCF)。因此,也许你的建议会奏效,我会尝试一下。你可以使用
    附件
    ,但在你与B建立关系之前,你需要这样做。好的,它奏效了。我已经编辑了你的答案,以包含我需要的确切代码。然而,我还是不太了解情况。对于依赖于上下文和类来跟踪更改和状态的普通EF来说,这似乎还可以。但是为什么它对STE不起作用呢?他们应该在没有现有背景的情况下跟踪变化,这正是自我跟踪背后的理念,不是吗?