使用AutoFac OnRelease保存DbContext更改?

时间:2014-01-21 21:51:24

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

我正在使用AutoFac在我的Web应用程序中注入具体的数据上下文。如果页面上没有例外,我想在请求结束时执行数据库上下文的SaveChanges()方法。否则我只想正常处理上下文。

我注意到AutoFac有一个OnRelease方法。该方法的Intellisense说明:

  

运行提供的操作,而不是在不再需要实例时处理它们。

因此,我正在考虑做这样的事情:

builder.RegisterType<MyContext>().As<IDbContext>().InstancePerHttpRequest()
    .OnRelease(x => {
        if (HttpContext.Current != null && HttpContext.Current.Error == null)
            x.SaveChanges();
        if (x != null)
        {
           x.Dispose();
           x = null;
        }
     });

这是否适合提交数据上下文的更改?它是否可以保证在每个请求上运行,即使出现异常?

2 个答案:

答案 0 :(得分:2)

一般情况下,我不喜欢在请求结束时保存更改的方法,因为这会失去灵活性。好的做法是保存业务交易结束时的变更。想象一下这个示例代码:

public ActionResult CreateAccount(CreateAccountModel createAccountModel)
{
    // Your business transaction start here from validating and processing input data
    var account = CreateAccountFrom(createAccountModel);
    _yourContext.Add(account);
    // Your business transaction ends here
    // This is the good place to commit your transaction
    _yourContext.SaveChanges();

    // You can have another business transaction here

    // the following is not important code to log the event
    // which could break the business transaction if it would be within one
    // you can wrap it in try-catch for example
    _yourContext.Add(new Event(){ Type = AccountCreated });
    _yourContext.SaveChanges();

    // code to retrieve date for the view
    var viewModel = GetViewModel();
    return View(viewModel);
}


现在关于你的代码,简而言之,它是一个保存更改的好地方。首先,你违反了单一责任原则,OnRelease应该在没有IDisposable的类上清理资源,但不要做其他逻辑。将业务逻辑放在那里只是因为你可以做到这一点是不好的。第二件事,如果你在x.SaveChanges()上得到一个例外,你的上下文就不会被处理掉。最好不要弄乱业务逻辑和对象生命周期逻辑。

答案 1 :(得分:0)

我同意亚历山大关于这是一个糟糕地方的结论,但我不同意他背后的理由。

我之所以不想这样做,是因为您的请求将结束并返回200响应。 OnRelease大概会在另一个线程上被调用,如果失败,那么您就不应该使用状态代码了。

EF本质上实现了工作单元模式,因此IMO不需要在任何地方调用保存更改。在请求结束时,保存一次就足够了。

我也不会在控制器方法上调用它,尤其是在大多数或所有控制器都访问dbcontext的情况下。 相反,您可以创建一个动作过滤器并将其全局连接。

这是一个.net核心示例:

public class SaveChangesFilter : IActionFilter
{
    private readonly ILogger<SaveChangesFilter> _logger;
    private readonly DbContext _dbContext;

    public SaveChangesFilter(IDbContext dbContext, ILogger<SaveChangesFilter> logger)
    {
        _logger = logger;
        _dbContext = dbContext as DbContext;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        if (_dbContext.ChangeTracker.HasChanges())
        {
            try
            {
                _dbContext.SaveChanges();
            }
            catch (Exception e)
            {
                context.Result = <insert error response here>

                _logger.LogCritical(e, e.Message);
            }
            finally
            {
                _dbContext.Dispose();
            }
        }
    }
}

然后在应用程序启动的ConfigureServices方法中:

services.AddMvc(options =>
                {
                    options.Filters.Add<SaveChangesFilter>();
                })