使用AutoFac OnRelease保存DbContext更改?

问题描述 投票:2回答:2

我正在使用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;
        }
     });

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

asp.net-mvc entity-framework dependency-injection autofac
2个回答
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()上得到一个例外,你的上下文就不会被处理掉。最好不要弄乱业务逻辑和对象生命周期逻辑。


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

然后在app启动ConfigureServices方法:

services.AddMvc(options =>
                {
                    options.Filters.Add<SaveChangesFilter>();
                })
© www.soinside.com 2019 - 2024. All rights reserved.