加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

实体框架Core 1.0工作单元与Asp.Net Core中间件或Mvc过滤器

发布时间:2020-12-15 22:40:10 所属栏目:asp.Net 来源:网络整理
导读:我正在使用EF Core 1.0(以前称为ad EF7)和ASP.NET Core 1.0(以前称为ASP.NET 5)的RESTful API. 我希望有一个单位的工作范围到一个http请求,在响应HTTP请求时,对DbContext所做的所有更改都将保存到数据库中,否则将保存(如果有的话)一些例外,例如). 在过去我已
我正在使用EF Core 1.0(以前称为ad EF7)和ASP.NET Core 1.0(以前称为ASP.NET 5)的RESTful API.

我希望有一个单位的工作范围到一个http请求,在响应HTTP请求时,对DbContext所做的所有更改都将保存到数据库中,否则将保存(如果有的话)一些例外,例如).

在过去我已经使用WebAPI2与NHibernate通过使用一个Action过滤器,我开始执行操作的事务,并且执行的操作我结束事务并关闭会话.这是http://isbn.directory/book/9781484201107推荐的方式

不过现在我正在使用Asp.Net Core(Asp.Net Core Mvc,尽管这不应该是相关的),而我理解的实体框架已经实现了一个工作单元.

我认为有一个中间件插入到ASP.NET管道(在MVC之前)将是正确的办法.所以请求会去:

PIPELINE ASP.NET:MyUnitOfWorkMiddleware ==> MVC控制器==>存储库==> MVC控制器==> MyUnitOfWorkMiddleware

我正在考虑如果没有异常发生,这个中间件保存DbContext的更改,所以在我的存储库实现中,我甚至不需要做dbcontext.SaveChanges(),而且一切都像一个集中式的事务.在伪码中,我猜想会是这样的:

class MyUnitOfWorkMiddleware
{
     //..
     1-get an instance of DbContext for this request.
     try {
         2-await the next item in the pipeline.
         3-dbContext.SaveChanges();
     }
     catch (Exception e) {
         2.1-rollback changes (simply by ignoring context)
         2.2-return an http error response
     }
}

这是否有意义?有人有什么类似的事情吗?我没有找到任何良好的做法或建议.

此外,如果我在我的MVC控制器级别使用这种方法,在POST新的资源时,将无法访问数据库创建的任何资源ID,因为在保存dbContext更改之前不会生成该ID(稍后在管道中)在控制器完成执行后的中间件中).如果我需要访问控制器中新创建的资源ID,该怎么办?

任何建议将不胜感激!

更新1:我发现使用中间件来实现这一点的问题,因为中间件中的DbContext实例与MVC(和存储库)生命周期中的实例不同.见问题Entity Framework Core 1.0 DbContext not scoped to http request

更新2:我还没有找到一个很好的解决方案.基本上这些是我的选择:

>尽快保存数据库中的更改.这意味着将其保存在存储库实现本身.这种方法的问题是,对于Http请求,也许我想使用几个存储库(即:将数据保存在数据库中,然后将BLOB上传到云存储),为了拥有工作单位,我必须实现一个处理多个实体或甚至多个持久性方法(DB和Blob存储)的存储库,其破坏了整个目的
>实现一个Action Filter,我将整个操作执行包在一个DB事务中.在控制器的动作执行结束时,如果没有异常,我向DB提交chanches,但如果有异常,我回滚并舍弃上下文.这样做的问题是,我的控制器的动作可能需要一个生成的实体的Id才能将其返回到http客户端(即:如果我得到一个POST / api / cars,我想返回一个201接受的位置标题,标识在/ api / cars / 123和Id 123创建的新资源将不可用,因为实体尚未保存在DB中,Id仍为临时0).控制器对POST动词请求的操作示例:

return CreatedAtRoute(“GetCarById”,new {carId = carSummaryCreated.Id},carSummaryCreated); //carSummaryCreated.Id将为0,直到更改保存在DB中

我如何将整个控制器的操作包装在一个DB事务中,同时可以使用数据库生成的任何Id,以便从控制器返回Http响应?或者..有没有任何优雅的方式来覆盖http响应,并在数据库更改被提交后将动作过滤器级设置为Id?

更新3:根据nathanaldensr的评论,我可以通过使用代码生成Guids来获得两个世界中最好的(将控制器的操作执行包装在DB事务_ UoW中,并且知道即将在数据库提交更改之前创建的新资源的Id)依靠数据库生成Guid.

解决方法

我也面临同样的问题,不知道应该遵循哪种方法.
我使用的方法之一是:
public class UnitOfWorkFilter : ActionFilterAttribute
{
    private readonly AppDbContext _dbContext;

    public UnitOfWorkFilter(AppDbContext dbContext,)
    {
        _dbContext = dbContext;
    }


    public override void OnActionExecuted(ActionExecutedContext context)
    {
        if (!context.HttpContext.Request.Method.Equals("Post",StringComparison.OrdinalIgnoreCase))
            return;
        if (context.Exception == null && context.ModelState.IsValid)
        {
            _dbContext.Database.CommitTransaction();
        }
        else
        {
            _dbContext.Database.RollbackTransaction();
        }
    }
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.HttpContext.Request.Method.Equals("Post",StringComparison.OrdinalIgnoreCase))
            return;
        _dbContext.Database.BeginTransaction();
    }}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读