【问题标题】:Singleton Scope for EF's DbContextEF 的 DbContext 的单例范围
【发布时间】:2015-05-23 02:35:23
【问题描述】:

所以我目前正在开发一个使用实体框架的 ASP.NET MVC Web 应用程序,我也在使用 Ninject 进行依赖注入。

所以,目前,这就是我向 Ninject 注册 DbContext 和服务的方式。

kernel.Bind<DbContext>().To<MyApplicationContext>().InSingletonScope();
kernel.Bind<IAccountService>().To<AccountService>().InSingletonScope();
kernel.Bind<IRegionService>().To<RegionService>().InSingletonScope();
kernel.Bind<IRoleService>().To<RoleService>().InSingletonScope();

我用InSingletonScope注册它们,这意味着它们只会被创建一次并在应用程序的整个生命周期中使用(至少我是这么理解的)。

控制器:

private IAccountService _accountService;

public MemberController(IAccountService accountService)
{
    _accountService = accountService;
}

但是,我深感这个单例范围会在我的 Web 应用程序中引起问题,尤其是实体框架的上下文,因为它是单例的。

因此,我已经面临一个小问题,如果我使用 SQL Management Studio 手动更新数据库,我的 Web 应用程序的实体框架中的数据在我重新启动应用程序之前不会更新(似乎是一些缓存机制英孚)。

--

但是,如果我删除 InSingletonScope,我会随机收到来自 EF 的错误消息:

一个实体对象不能被多个 IEntityChangeTracker 实例引用

我理解为什么会发生这种情况,因为 AccountService 初始化的 DbContext 可能与 RegionService 不同。但我不知道如何解决这个问题。

我对依赖注入的理解还很有限,有大神可以指教一下吗?

--

编辑:我已尝试将所有注射更改为 InRequestScope,但我仍然得到 ​​p>

一个实体对象不能被多个 IEntityChangeTracker 实例引用

当尝试从我的应用程序中的另一个服务插入具有相关对象(外键)的新实体时。这意味着他们仍在使用不同的 DbContext,发生了什么?!

最终编辑:好的,我找到了问题,是我的缓存机制缓存了先前的请求,导致所有后续请求出现关系问题。

【问题讨论】:

  • 我认为您正在体验Captive Dependencies。您可能将 DbContext 注入到注册为单例的使用者中,导致 DbContext 也成为单例。
  • @Steven 你指的是空导航属性问题吗?我已经把所有的消费者都改成了 RequestScope 了,为什么还是单例呢?
  • 我不熟悉导航属性问题,所以我无法对此发表评论,我无法说明为什么以及您的 DbContext 是否被消费者保持活跃,因为您没有t 发布了任何重现该问题的代码。 Ninject 缺乏为您找到它的工具;你将不得不调试这个。

标签: c# asp.net-mvc entity-framework dependency-injection ninject


【解决方案1】:

包括DbContext在内的部分服务的lifetime可以这样配置:

services.AddDbContext<ApplicationDbContext>(
    options => { options.UseSqlServer("YourConnectionString"); },
    ServiceLifetime.Singleton);

REF

【讨论】:

  • 这可行,但不是一个好主意,尤其是在 ASP.NET 应用程序中。
  • 我知道 DBContext 作为 Singleton 是一个非常糟糕的主意,但在我的情况下,即使在请求结束后由于长时间处理,我也需要保持我的服务运行......并且因为 dbcontext 将在我的请求后处理最后,我尝试将其生命周期更改为单例,它的工作原理正是我所期望的。问题是,为什么它这么糟糕?如果不应该是单例,我如何在请求结束后访问 dbcontext?
【解决方案2】:

单例范围对您的上下文来说是一个非常糟糕的主意。请求范围是您应该使用的,因为它本质上是请求生命周期中的单例。

至于为什么在使用请求范围时会出错,我不能肯定地说。假设您使用的实体都来自相同的上下文类型,并且您在需要的任何地方都正确地注入了上下文,那么永远不应该有多个上下文实例在起作用。

编辑

重新阅读您的问题后,听起来好像您的服务实际上是在其构造函数或其他东西中初始化上下文。如果是这样,那是你的问题。您的上下文应该被注入到您的服务中,即:

public class AccountService : IAccountService
{
    protected readonly DbContext context;

    public AccountService(DbContext context)
    {
        this.context = context;
    }

    ...
}

然后,Ninject 将在更新任何服务时正确地注入MyApplicationContext 的请求范围实例。

【讨论】:

  • 是的,这就是我对我的服务所做的,注入 DbContext。所以你是说我应该使用InRequestScope() 来注册Ninject 的所有服务,包括EF 的DbContext?这会导致什么其他问题?我在原始问题中陈述的不同 ObjectContext 问题可以吗?
  • 对于 Web 应用程序而言,几乎所有内容都应该是请求范围的。由于控制器之类的东西都会随着每个请求而更新和处理,因此您将始终获得一个新的服务副本,或者在您在应用程序的其他区域之间进行的任何传递以完成请求的任何内容。
  • 我已经尝试过了,看看我的问题编辑。真的很奇怪的问题,实体的关系有时没有填充(尤其是第一次运行)并变为空..
【解决方案3】:

Dan,当您为整个应用程序限定单个 DBContext 时,您正在制造瓶颈。在底层,Entity Framework 将相当有效地处理您需要的对象数量。如果您深入了解内部结构,则与数据库联系的实际对象会做同样的事情。因此,您通过创建单例进行优化的尝试实际上可能会产生一个非常大的问题。

【讨论】:

    【解决方案4】:

    我终于设法通过使用InRequestScope 而不是InSingletonScope 解决了这个问题。

    最初,由于我的服务层上存在缓存机制,我在更改为InRequestScope 后仍然面临同样的问题。

    因此,所有后续请求都使用最初缓存的实体对象,这就是我从 EF 收到多个实例错误的原因。

    --

    如果你仍然面对

    一个实体对象不能被多个 IEntityChangeTracker 实例引用

    更改为InRequestScope 后出现错误,请确保您的实体未以某种方式缓存或存储以供后续 HTTP 请求使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多