首页 文章

对DbContext实例有一个强制依赖是不是很糟糕?

提问于
浏览
0

也许这个问题有一些解释但无法找到最佳解决方案:

我正在阅读马克·西曼关于俘虏依赖性的这个blog post,据我所知,在帖子结尾时他得出结论从不使用或至少试图避免俘虏依赖,否则会有麻烦(到目前为止还可以) . 这是Autofac文档中的另一个post .

他们建议只使用 purpose (当你知道自己在做什么时!) . 这让我想起了我在网站上遇到的情况 . 我有10个服务,所有这些服务都依赖 DbContext 进行数据库操作 . 我认为如果我解决 DbContext 的问题而不是将其永久保存在附加到我的服务的内存中,他们可以很容易地注册为 InstancePerLifetimeScope . (我在我的情况下使用Autofac) . 所以,我认为一个很好的起点是根据生命周期实例创建所有这些,并根据请求创建 DbContext 实例 . 然后在我的服务中,我会使用类似的东西:

public class MyService
{
    private readonly IDbContext _dbContext = DependencyResolver.Current.GetService<IDbContext>();

    private MyModel GetMyModel() => Mapper.Map<MyModel>(_dbContext.MyTable.FirstOrDefault());
}

然后在我的启动课上我有:

builder.RegisterType<ApplicationDbContext>().As<IDbContext>().InstancePerRequest();
builder.RegisterType<MyService>().As<IMyService>().InstancePerLifetimeScope();

这种模式是否正常工作,我的意思是不保持 dbContext 永远附加到任何服务,因此它将在请求结束时处理,如果它有效,是否存在任何性能问题:

private readonly IDbContext _dbContext = DependencyResolver.Current.GetService<IDbContext>();

与构造函数注入相比(从dbContext到数据库有很多调用,所以每次我想使用它时都害怕得到 IDbContext ,因为它可能消耗资源)?

我希望 dbContext 是每个请求的实例而不是每个依赖实例的原因是我在 dbContext 对象之上实现了工作单元模式 .

我的控制器中的常规方法如下所示:

public ActionResult DoSth()
{
     using(var unitOfWork = UnitOfWorkManager.NewUnitOfWork())
     { 
          //do stuff
          try
          {
              unitOfWork.Commit();
              return View();
          }
          catch(Exception e)
          {
              unitOfWork.RollBack();
              LoggerService.Log(e);
              return View();
          }
     }
}

如果这样可以正常工作那么我还有另外一个问题 . 因此,如果我可以将我的服务作为每个生命周期的实例(DbContext除外),那么在服务内部的每个方法上应用async-await是否存在任何问题,以使它们成为非阻塞方法 . 我问这个是否有任何问题使用async-await为 dbContext 实例,所以,例如,我会有这样的事情:

public async MyModel GetMyModel()
{
   var result = //await on a new task which will use dbcontext instance here
   return Mapper.Map<MyModel>(result);
}

任何建议或意见非常感谢!

2 回答

  • 0

    我会远距离接近这个问题 .

    有一些建筑选择可以让你的生活更轻松 . 在Web开发中,将应用程序设计为 stateless service layer (所有状态都保存在DB中)并符合 one HTTP request, one business operation 原则(换句话说,一个控制器操作的一个服务方法)是切实可行的 .

    我不知道你的架构看起来如何(你的帖子中没有足够的信息来确定),但它很可能符合我上面描述的标准 .

    在这种情况下,很容易决定选择哪个组件生命周期:DbContext和服务类可以是瞬态的(Autofac术语中的InstancePerDependency)或每个请求(InstancePerRequest) - 它并不重要 . 关键是他们有相同的生命周期,所以 the problem of captive dependencies doesn't arise at all .

    以上的进一步含义:

    • 您可以在服务类中使用ctor注入而无需担心 . (无论如何,在调查像lifetime scopes and IOwned<T>这样的生命周期控制可能性之后,服务定位器模式将是最后一个选项 . )

    • EF本身通过SaveChanges实现工作模式,适用于大多数情况 . 实际上,如果由于某种原因其事务处理无法满足您的需求,您只需要在EF上实施UoW . 这些是特殊情况 .

    [...]是否有任何问题要对服务内部的每个方法应用async-await,以使它们成为非阻塞方法 .

    如果你一直应用async-await模式(我的意思是等待所有异步操作)直接到你的控制器动作(返回Task <ActionResult>而不是ActionResult),那就没有问题了 . (但是,请记住,在ASP.NET MVC 5中,异步支持不完整 - async child actions are not supported . )

  • 1

    答案一如既往,取决于......如果符合以下条件,此配置可以正常工作:

    • 您的范围是在请求边界内创建的 . 您的工作单位是否在创建范围?

    • 在创建范围之前,您无法解析任何InstancePerLifetimeScope服务 . 否则,如果在请求中创建多个范围,它们的寿命可能会超过应有的时间 .

    我个人只是建议制作任何依赖于DbContext的东西(直接或间接)InstancePerRequest . 瞬态也会起作用 . 您肯定希望一个工作单元内的所有内容都使用相同的DbContext . 否则,使用Entity Framework的第一级缓存,您可能有不同的服务检索相同的数据库记录,但如果它们不使用相同的DbContext,则在不同的内存中副本上运行 . 在这种情况下,最后更新将获胜 .

    我不会在MyService中引用你的容器,只是构造函数注入它 . 您的域或业务逻辑中的容器引用应谨慎使用,并且仅作为最后的手段 .

相关问题