【问题标题】:Concurrency in ASP .NET MVC 5 with Entity Framework带有实体框架的 ASP .NET MVC 5 中的并发性
【发布时间】:2016-05-15 06:05:03
【问题描述】:

我正在使用带有实体框架的 ASP .NET MVC 5 中的简单 CMS。 我有几个关于 MVC 应用程序中的并发性的问题。

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

  • 所有数据(帖子、类别信息、标签、文档)都存储在数据库中(使用实体)

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

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

然后是我的管理部分(需要验证):

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

我知道安全系统需要一些安全机制,但是你能帮我怎么做吗?

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

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

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

非常感谢您的回答!

【问题讨论】:

标签: c# asp.net asp.net-mvc entity-framework asp.net-mvc-5


【解决方案1】:

对于您关于用户仅检索/写入数据的第一个问题,并发将不是问题。他们永远不会尝试同时更新/删除一行。管理部分是另一回事。您预计多个管理员用户将同时使用数据库,这将导致 admin1 抓取记录并对其进行编辑,但当它重新插入该记录时,它已被 admin2 更新。有两种处理方法:

  1. 悲观并发:这意味着您正在对数据库的某些记录使用锁。当一个进程正在更新某个记录时,这些行会被锁定,同时没有其他进程可以编辑它。不幸的是,Entity Framework 不支持此功能。
  2. 乐观并发:受 EF 支持,这意味着您的数据库中有一个带有 rowversion 的额外列。当一个进程试图重新插入一条记录时,它会首先检查 rowversion 是否没有改变。如果相同,则插入记录。如果它已更改,则将重新获取记录并再次编辑值。有关乐观并发的 Msdn 文档here

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

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

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

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

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

【讨论】:

    【解决方案2】:

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

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

    我知道安全系统需要一些安全机制, 但是你能帮我怎么做吗?

    您可以在其他类中编写查询,例如存储库和/或服务。在google上搜索“repository and services 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: correct use of DBContext class inside your repository class

    希望这会有所帮助!

    【讨论】:

      【解决方案3】:

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

      • 写入和更新:

        • 表包含一些唯一约束,因此您检查一个实体是否存在,如果不存在则尝试创建一个。现在两个并发操作执行检查,都得到空结果,并且都尝试创建实体。一个操作会因异常而失败,而另一个操作则更幸运,并首先创建了实体。

        • 表包含外键:通过创建新实体,其他一些操作已删除相关条目。结果 - 异常。

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

      因此,您必须考虑应用程序/数据库的结构和场景,并决定是否要采取措施防止不受控制的并发更新/写入。

      你可以做什么?

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-02-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-21
        • 2014-04-23
        相关资源
        最近更新 更多