带有entity framework的ASP .NET MVC 5中的并发性

我正在使用Entity Framework在ASP .NET MVC 5中使用简单的CMS。 我对MVC应用程序中的并发性有一些疑问。

首先 – 我的应用程序的公共部分(没有任何身份validation的用户部分):

  • 所有数据(post,类别信息,标签,文档)都存储在DB中(使用实体)

  • 在公共部分的控制器中,只有DB的读取或写入数据,而不是删除或编辑

所以我的第一个问题 – 是否有必要采用一些机制来避免公共部分的并发性危险? 当多个用户同时浏览我的网站时,一切都会正确吗?

然后我的管理部分(需要validation):

  • 可能有多个用户具有多个角色(但实际上只有很少的注册用户)
  • 用户可以在DB中创建/编辑/删除数据

我知道有一些安全机制对于拥有安全系统是必要的,但是你能帮我解决这个问题吗?

我也在每个方法中使用这种模式用于DbContext,其中DB是必需的:

using (var db = new CmsContext()) { // other stuff here }

而不是在所有方法中使用的一个类变量db。 这是正确的吗?

非常感谢您的回答!

是否有必要采用一些机制来避免公共部分的并发危险? 当多个用户同时浏览我的网站时,一切都会正确吗?

据我所知,并发性与两个人同时编辑同一条记录有关。 考虑到公共部分没有任何写入方法,避免并发机制是不必要的,也是不可能的。

我知道有一些安全机制对于拥有安全系统是必要的,但是你能帮我解决这个问题吗?

您可以在其他类中编写查询,例如存储库和/或服务。 在谷歌搜索“存储库和服务entity framework”,我发现这个链接http://techbrij.com/service-layer-entity-framework-asp-net-mvc-unit-testing ,听起来很有趣。

我也在每个方法中使用这种模式用于DbContext,其中DB是必需的:

 using (var db = new CmsContext()) { // other stuff here } 

而不是在所有方法中使用的一个类变量db。 这是正确的吗?

这是一个很难的问题。 现在很难说某些事情是对还是错。 有很多人,有几种不同的意见。 在我看来,你的例子不是最好的方法(但这并不意味着它是不正确的)。 使用存储库类时,我们通常只有一个DbContext实例。 看看这个线程c#entity framework:在您的存储库类中正确使用DBContext类

希望这可以帮助!

关于用户只检索/写入数据的第一个问题,并发性不会成为问题。 他们永远不会尝试同时更新/删除单个行。 管理部分是另一个故事。 您希望多个管理员用户同时使用该数据库,这将导致admin1抓取记录,编辑它的条件,但是当它重新插入记录时,它已经由admin2更新。 有两种方法可以解决这个问题:

  1. 悲观并发:这意味着您在数据库的某些记录上使用锁。 当一个进程正在更新某个记录时,这些行会被锁定,而其他进程也无法同时编辑它们。 不幸的是,entity framework不支持此function。
  2. 乐观并发: EF支持这一点 ,这意味着您在数据库中有一个带有rowversion的额外列。 当进程尝试重新插入记录时,它将首先检查rowversion是否未更改。 如果它是相同的,则插入记录。 如果更改了记录,则会重新获取记录并再次编辑值。 关于乐观并发的Msdn文档。

在您的代码模型中,您可以使用[TimeStamp]属性定义额外属性:

 [TimeStamp] public virtual byte[] RowVersion {get; set;} 

或者在流畅的API中,您可以像这样映射它:

 modelBuilder().HasProperty(p => p.RowVersion).IsRowVersion(); 

您还可以将整行用作“rowversion”来检查更改,但通常会使用额外的列。 否则,如果您只想更新1个字段,则必须将整行发送到数据库,因为需要检查rowversion。 另请注意, [TimeStamp]仅适用于字节数组,如果要使用其他类型,则必须使用[ConcurrencyCheck]属性。 如果将整行用作rowversion,则必须将此属性应用于所有属性。

首先,您应该了解并发可能导致exception的所有情况。 一些常见的案例,通常是关系和合同。

  • 写作和更新:

    • 表包含一些唯一约束,因此您检查实体是否存在,并尝试创建实体(如果不存在)。 现在两个并发动作执行检查,两者都获得空结果,并且两者都尝试创建实体。 一个操作将失败并出现exception,而另一个操作获得更多运气,并首先创建实体。

    • 表包含外键:通过创建新实体,其他一些操作已删除了依赖项。 结果 – 例外。

并发更新任何标量值都不会导致任何exception,但最后一个会获胜。

因此,您必须考虑app / db和scenario的结构,并决定是否要采取措施防止不受控制的并发更新/写入。

你可以做什么反对?

 public class FooService { private static object Obj = new object(); public void Create(Foo foo) { lock (Obj) { // check + create } } } 

这里的问题可能是你有多个实例,因为它只是服务器级锁。

交易

数据库锁

 using (var ctx = new DbCtx) { ctx.Database.Connection.Open(); using (var transaction = ctx.Database.BeginTransaction(IsolationLevel.Serializable)) { try { // check + update transaction.Commit(); } catch (Exception) { transaction.Rollback(); } } ctx.Database.Connection.Close(); }