当前位置: 首页 > 知识库问答 >
问题:

每个web请求一个DbContext…为什么?

澹台华晖
2023-03-14

为什么首先这是一个好主意?通过使用这种方法,您获得了哪些优势?在某些情况下,这是一个好主意吗?在每个存储库方法调用实例化dbcontexts时,使用此技术是否可以执行一些不能执行的操作?

共有1个答案

李言
2023-03-14

注意:这个答案谈到了实体框架的dbcontext,但它适用于任何类型的工作实现单元,例如LINQ to SQL的dataContext和NHibernate的isession

让我们从回显Ian开始:为整个应用程序使用单个dbcontext是一个坏主意。唯一有意义的情况是,您有一个单线程应用程序和一个仅由该应用程序实例使用的数据库。dbcontext不是线程安全的,而且由于dbcontext缓存数据,它很快就会过时。当多个用户/应用程序同时在数据库上工作时(当然,这是很常见的),这会给您带来各种麻烦。但是我希望您已经知道了这一点,并且只想知道为什么不将dbcontext的新实例(即具有临时生活方式)注入到需要它的任何人中。(有关为什么单个dbcontext(甚至每个线程的上下文)是不好的更多信息,请阅读以下答案)。

首先,我要说的是,将dbcontext注册为transient可能会起作用,但通常您希望在某个范围内拥有这样一个工作单元的单个实例。在web应用程序中,在web请求的边界上定义这样的作用域是可行的;因此是一种按Web请求的生活方式。这允许您让一整套对象在同一上下文中操作。换句话说,它们在同一业务事务中进行操作。

如果您没有在同一上下文中运行一组操作的目标,那么在这种情况下,暂时的生活方式是很好的,但是有一些事情需要注意:

  • 由于每个对象都有自己的实例,所以每个更改系统状态的类都需要调用_context.saveChanges()(否则更改将丢失)。这可能使您的代码复杂化,并为代码增加了第二个责任(控制上下文的责任),并且违反了单一责任原则。
  • 您需要确保[由dbcontext]加载和保存的实体永远不会离开此类类的范围,因为它们不能在另一个类的上下文实例中使用。这会极大地使代码复杂化,因为当您需要这些实体时,您需要再次按id加载它们,这也可能导致性能问题。
  • 由于dbcontext实现了IDisposable,您可能仍然希望处置所有创建的实例。如果你想这样做,你基本上有两种选择。您需要在调用context.saveChanges()之后立即在同一方法中处理它们,但在这种情况下,业务逻辑将获得它从外部传递的对象的所有权。第二种选择是在Http请求的边界上处置所有创建的实例,但在这种情况下,您仍然需要某种范围来让容器知道何时需要处置这些实例。

另一种选择是根本不注入dbcontext。相反,您可以注入一个dbcontextFactory,它能够创建一个新的实例(我过去经常使用这种方法)。通过这种方式,业务逻辑显式地控制上下文。If可能如下所示:

public void SomeOperation()
{
    using (var context = this.contextFactory.CreateNew())
    {
        var entities = this.otherDependency.Operate(
            context, "some value");

        context.Entities.InsertOnSubmit(entities);

        context.SaveChanges();
    }
}

由于这种工厂方法的缺点,对于更大的系统来说,另一种方法可能是有用的,那就是让容器或基础结构代码/组合根管理工作单元的方法。这就是你的问题的风格。

通过让容器和/或基础结构处理这一点,您的应用程序代码不会因为必须创建、(可选地)提交和处置UoW实例而受到污染,这使业务逻辑保持简单和干净(只是一个单独的责任)。这种方法有一些困难。例如,您是否提交并处置了该实例?

处理一个工作单元可以在web请求结束时完成。然而,许多人错误地认为这也是提交工作单元的地方。但是,在应用程序中的那一点上,您无法确定工作单元实际上应该被提交。例如,如果业务层代码抛出了一个在调用堆栈的更高层捕获的异常,那么您肯定不想提交。

class TransactionalCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    readonly DbContext context;
    readonly ICommandHandler<TCommand> decorated;

    public TransactionCommandHandlerDecorator(
        DbContext context,
        ICommandHandler<TCommand> decorated)
    {
        this.context = context;
        this.decorated = decorated;
    }

    public void Handle(TCommand command)
    {
        this.decorated.Handle(command);

        context.SaveChanges();
    } 
}
 类似资料:
  • 为什么这是一个好主意呢?您通过使用这种方法获得了什么好处?在某些情况下,这会是一个好主意吗?在每个存储库方法调用实例化时,您是否可以使用这种技术做一些不能做的事情?

  • 问题内容: 我们有一个weblogic批处理应用程序,它可以同时处理来自使用者的多个请求。我们使用log4j记录目的。现在,我们登录到单个日志文件以处理多个请求。调试给定请求的问题变得很麻烦,因为所有请求都将日志记录在一个文件中。 因此,计划是每个请求只有一个日志文件。使用者发送一个请求ID,必须对其进行处理。现在,实际上可能有多个使用者将请求ID发送到我们的应用程序。因此,问题是如何根据请求隔离

  • 在上面的代码示例中,试图为每个请求生成唯一的txn-id。但是,观察到它正在重复使用相同的数字导致重复。我还尝试使用以下方法根据当前时间生成,当在1秒内触发超过1个请求时,它仍然会导致重复。 任何替代解决方案来为每个请求生成唯一ID,而不管并发情况如何? 提前感谢。

  • 问题内容: 我刚刚启动了一个简单的Java测试项目,该项目使用Hibernate管理某些实体,并提供REST接口来操纵这些对象并提供一些其他业务逻辑。REST接口是使用RESTEasy和Jetty创建的。 到目前为止,一切工作正常,但是我感觉我实际上写了太多样板代码。由于我在这些Java框架中没有太多经验,所以我只是想知道是否有人可以给我提示如何改善这种情况。 每个请求创建hibernate会话

  • 问题内容: 我已经阅读了有关node.js和其他服务器(例如Apache)的信息,其中线程有所不同。我根本不了解线程的含义。 如果我有一个运行SQL的网页来访问数据库,请在一个服务器端页面中说三个不同的数据库,这对node.js中的线程意味着什么?阿帕奇?“线程”在这里是什么意思? 或者作为我看到的文章,“启动一个新线程来处理每个请求”。 说Apache为每个请求生成一个线程,但node.js却没

  • 我尝试分析一个gRPC java服务器。我主要看到下面的一组线程池。 null