多个链接的1-1关系不会按预期同步PK

时间:2012-03-27 18:01:45

标签: entity-framework entity-framework-4 primary-key foreign-key-relationship cascade

我尝试搜索SO,但我发现的所有结果似乎都涉及更新已经持久保存到数据库的实体的PK。我的情况不同。

我在数据库中有3个表,有1-0..1的关系。关系看起来像这样:

A <- B <- C

其中'&lt; - '表示关系并指向主要结尾。即每个B总是有一个相关的A,但A可以没有B.换句话说,A的基数是1而B的是0..1。

每个关系由FK表示,FK从子实体的PK到父实体的PK。每个PK都是uniqueidentifier Id列,具有客户端生成的值。我从数据库中生成了一个EF 4模型,该模型与基数具有相同的关系。

我正在尝试将儿童访问者BC添加到现有的A实体中。出于设计原因,在一个代码的代码中创建了几个新实例,并且A实体链接到另一个实体中的B实体。此外,我不希望后者知道C存在。

以下是BC创建代码的显示方式:

public B CreateB()
{
  return new B
    {
      Id = Guid.NewGuid(),
      C = new 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的Entity类作为生成实体的基类)和自跟踪实体代码生成器。结果是一样的。

所以,代码崩溃了。原因可能是在AB被链接后,BC会获得不同的PK值,这对于具有1-1关系的实体来说是非法的。< / p>

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

我不能通过为FK使用单独的PKs列来破坏密钥链,因为EF要求1-1关系的两边都是PK。

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

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

我有两个问题:

  1. 由于某些原因,我对我案例级联PK更新的期望是否错误? (虽然看起来奇怪)即,它是一个错误还是一个功能?
  2. 除了修改STE模板之外,还有其他任何更简单的方法来解决问题吗?也许在EF映射或上下文中有一些神奇的选项?
  3. 提前致谢。

1 个答案:

答案 0 :(得分:3)

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

不幸的是,你不这样做。相反,你创建了一个与A的附加关系,但然后撒谎到上下文并声称一切都已修复。更确切地说,你调用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之间的关系无关。

简而言之,请注意Attach。你打电话时需要知道自己在做什么。

<强>更新

处理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();
  }
}