实体框架多对多自我关系和乐观并发控制

时间:2015-04-22 16:11:45

标签: c# entity-framework optimistic-concurrency

我有一个拥有多对多自我关系的实体。作为一个例子,考虑这个实体:

public class User
{
    public int ID { get; set; }
    public string UserName { get; set; }

    public virtual ICollection<User> Friends { get; set; }
}

以下是我配置映射的方法:

HasMany(t => t.Friends).WithMany()
    .Map(m => { 
        m.MapLeftKey("UserID");
        m.MapRightKey("FriendID");
        m.ToTable("UserFriends");
        });

由于此关系现在由EF管理,因此我无法在代码中访问UserFriends DbSet,无法处理对其的并发访问。 为了使这个组合处理并发访问(添加/删除),我是否需要自己处理多对多关系,然后添加[Timestamp]列,或者有一种方法告诉EF同时处理这个本身?与模型构建器中的配置类似。

编辑:我正在使用EF 6,目前如果实体上有并发操作(例如,尝试删除当前不在数据库上退出的朋友),我会收到以下错误消息, DbUpdateException

  

保存不公开外键的实体时发生错误   他们关系的属性。 EntityEntries属性将   返回null,因为无法将单个实体标识为源   例外。可以在保存时处理异常   通过在实体类型中公开外键属性更容易。看到   InnerException以获取详细信息。

2 个答案:

答案 0 :(得分:3)

乐观并发不适用于此。

联结表永远不会更新。其记录可以添加或删除。这意味着没有需要rowversion的CRUD操作。

事实上,并发性相当容易:

  • 两个并发用户无法添加相同的关联,因为最后一个用户将遇到唯一的密钥违规。
  • 两个并发用户无法删除相同的关联,因为最后一个用户将看到一个异常,即意外数量的记录(0)受到影响。
  • 外键问题(添加/删除与同时删除的实体的关联)。这些也会引发异常。

因此,归结为处理异常并将其转换为可理解的用户反馈。所有这些情况也必须在更新(和乐观并发)确实发挥作用的情况下处理。

答案 1 :(得分:1)

虽然UserFriends表上没有rowversion列,但EF仍然能够识别DbContext.SaveChanges失败的原因是多对多关系时的并发问题。在这种情况下,ef会抛出使用DbUpdateException的OptimisticConcurrencyException异常。使用以下代码来捕获它:

try
{
    context.SaveChanges();
}
catch (DbUpdateException ex)
{
    if(ex.InnerException is OptimisticConcurrencyException)
    {
        // If you are here then there was concurrency problem in many-to-many relationship
    }
}

您可能无法将当前流畅的api配置为自动包含在UserFriends表rowversion列中,但是在生成迁移后,您可以手动添加到rowversion列的UserFriends CreateTable语句定义中:

CreateTable(
    "dbo.UserFriends",
    c => new{
        RowVersion = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
        // other columns definitions
    });

然而,当多对多关系出现并发问题时,这不会改变DbContext行为。