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



我有一个具有多对多自关系的实体。举个例子,考虑一下这个实体:

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管理,我在代码中实际上没有访问UserFriendsDbSet的权限,也无法处理对它的并发访问。为了让这个组合处理并发访问(添加/删除),我是否需要自己处理多对多关系,然后添加一个[Timestamp]列,或者有没有方法告诉EF自己同时处理这个关系?就像模型生成器中的配置一样。

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

保存不公开外键的实体时出错属性。EntityEntries属性将返回null,因为无法将单个实体标识为源的异常。可以在保存时处理异常通过在实体类型中公开外键属性更容易。看见有关详细信息,请参见InnerException。

这里不适用乐观并发。

结表永远不会更新。它的记录要么被添加,要么被删除。这意味着不存在需要rowversion的CRUD操作。

所以事实上,并发是相当容易的:

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

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

尽管UserFriends表上没有rowversion列,EF仍然能够识别出DbContext.SaveChanges失败的原因是在多对多关系的情况下并发问题。在这种情况下,ef抛出OptimisticConcurrentException异常,该异常与DbUpdateException一起不透明。使用以下代码捕获它:

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
}
}

您可能不会将当前fluent api配置为自动包含在UserFriends表的rowversion列中,但在生成迁移后,您可以手动添加到rowversion列的UserFriendsCreateTable语句定义中:

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

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

最新更新