【问题标题】:Calling SaveChanges once per request in Asp.Net MVC application在 Asp.Net MVC 应用程序中每个请求调用一次 SaveChanges
【发布时间】:2018-06-23 02:40:50
【问题描述】:

我正在尝试使用 Asp.Net MVC 和 Entity Framework 创建一个最简单的 Web 应用程序。我实际上用它完成了几个项目,但对代码并不满意,所以我正在遵循一些更流行的方法来完成它。这是我的结构:

  • 控制器获取请求,
  • 我有包含业务逻辑的服务。他们还使用我的数据库上下文来更改数据库中的数据。我不想创建另一个数据库层,所以我正在使用它们,
  • 我将我的服务注入到控制器(使用 Unity)并调用像 CustomerService.Delete(Id) 这样的东西
  • 我的服务根据 Id 删除数据。

所以我的控制器不包含任何逻辑或数据库操作,我的服务确实包含两者。我认为这是做事的好方法,但我有一个问题。

假设我第一次添加一个客户,并且有另一张桌子保持他们的余额,我想在第一次注册时包含 10 美元的奖金,当我调用我的 CustomerService.Add(Customer) 时,该方法也调用 CustomerService.AddBalance(Customer, 10)。在这些方法中我调用DbContext.SaveChanges,这是问题所在,我调用SaveChanges 2 次,如果CustomerService.AddBalance(Customer, 10) 由于某种原因失败,我仍然有客户数据,但没有余额。我知道我可以使用交易,但我该把代码放在哪里?如果我知道在请求完成之前有一个地方最后运行,我可以在那里打电话给SaveChanges(),它会工作,但我认为这也不是一个好主意。

基本上我想为每个请求拨打一次SaveChanges(),但我找不到合适的地方。

谢谢

【问题讨论】:

  • 您可以一次添加到两个表并调用 SaveChanges 一次。它应该工作

标签: asp.net-mvc entity-framework dependency-injection transactions


【解决方案1】:

如果您正在使用您希望使用事务的数据库进行任何工作。如果你不这样做,如果你的一个调用失败,你将使你的数据库处于不一致的状态。使用事务,在调用 Commit 之前不会在数据库中提交任何内容。您可以随心所欲地调用 SaveChanges,它不会产生任何影响。

您可以在属性中创建 TransactionScope,然后将其添加到您正在访问数据库的任何控制器操作中。这是我用的一个

[AttributeUsage(AttributeTargets.Method)]
public class TransactionAttribute : ActionFilterAttribute
{
    private TransactionScope TransactionScope { get; set; }

    public override void OnActionExecuting(HttpActionContext filterContext)
    {
        TransactionScope =
            new TransactionScope(TransactionScopeOption.Required, new TransactionOptions
            {
                IsolationLevel = IsolationLevel.ReadCommitted
            });
    }
    public override void OnActionExecuted(HttpActionExecutedContext filterContext)
    {
        if (TransactionScope == null)
            return;

        if (filterContext.Exception == null)
        {
            TransactionScope.Complete();
            return;
        }

        TransactionScope.Dispose();
    }
}

您可以将事务代码放在 BeginRequest 中,将其存储在 HttpContext 中并在 EndRequest 上提交。这将为 asp.net 处理的每个调用创建一个 TransactionScope。

【讨论】:

    【解决方案2】:

    根据文档 EF 默认情况下将所有更改设置为事务中的数据库,直到调用 SaveChanges()。 因此,您可以将添加到数据库的逻辑提取到单独的方法中,创建单独的方法来保存对 DB 的更改并设计您的代码如下:

    • AddCustomer 到数据库(不要调用保存更改)
    • AddBalance 到 DB(不要调用保存更改)
    • SaveChanges 在你的服务方法的末尾

    这将保证所有事情都发生在事务中,并且在发生任何错误时不会应用对数据库的更改。

    【讨论】:

      【解决方案3】:

      我发现为你的上下文创建一个扩展(我假设你正在使用 EF)是一个好方法:

      public static class TIContextCompleteExtention
      {
          public static void Complete(this TIContext context)
          {
              try
              {
                  context.SaveChanges();
              }
              catch (Exception ex)
              {
                  throw ex;
              }
          }
      
          public static async Task CompleteAsync(this TIContext context)
          {
              try
              {
                  await context.SaveChangesAsync();
              }
              catch (Exception ex)
              {
                  throw ex;
              }
          }
      }
      

      上面的 TIConext 来源于 EF 的 dbContext。

      无论您需要在哪里完成您的交易使用

      _context.Complete();或 _context.CompleteAsync() 而不是 EF 的 SaveChanges() / SaveChangesAsync()

      如果你以后换掉你的 ORM,你需要做的就是改变上面的扩展来适应你的新上下文。

      希望这会有所帮助。

      【讨论】:

        猜你喜欢
        • 2016-08-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-04
        • 2017-06-16
        • 1970-01-01
        相关资源
        最近更新 更多