依赖项注入DbContext始终为null

时间:2018-11-28 10:30:17

标签: c# entity-framework azure dependency-injection async-await

我已经在多个ASP.NET(核心)应用程序中使用了DI,这些应用程序将DbContext注入到控制器构造函数中,并且在对控制器的第一个请求时,构造函数被运行并注入了依赖项。

我现在正在使用一个Web API项目,该项目已连接到Azure消息总线,处理和持久存储总线中的数据,并提供了数据的API终结点。处理和持久化来自Azure Message Bus的消息的类是QueueProcessor

我需要从项目开始就保留数据,这意味着我需要一个从项目运行时开始的DbContext实例,而不是从API端点查询数据时的实例。因此,永远不会隐式调用QueueProcessor的构造函数,如果要手动执行此操作,我需要一个预先存在的MyDbContext实例传递给它。

我已经研究了几天,尝试了一些手动模式,但是我遇到了并发问题,整个项目目前感觉像是黑客。

The closest question I've come across is this, which despite specifying non-controllers, the accepted answer requires a controller for the dependency to be injected.

这是我目前正在做的事情:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHangfire(x => x.UseSqlServerStorage(Configuration.GetConnectionString("DefaultConnection")));

        services.AddDbContextPool<MyDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("default")));

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }

然后,我必须创建我的QueueProcessor的新实例,并在构造函数中使用var context = new MyDbContext()。这会导致并发问题,并完全否定DI

如果像我已经做过数百遍一样,要向DI注入MyDbContext,则创建类的实例以在QueueProcessor内运行进程意味着我必须传递{{ 1}}给我自己的构造函数。 VS在设计时将其标记出来。

我曾经尝试过这样做:

MyDbContext

我正在寻找一种更清洁的实现方式:

  • services.AddTransient(x => new QueueProcessor(x.GetService<MyDbContext>(), Configuration.GetConnectionString("MessageBus"))); 中调用一个方法以从项目开始处理Azure Message Bus消息。
  • 在处理消息之前,我应该有一个QueueProcessor的实例化实例,可用于在处理消息时将其持久化。
  • 不必使用MyDbContext来正确设置DI。

是否有办法实现这一目标,或者我完全错过了这里的成绩?

1 个答案:

答案 0 :(得分:1)

有效的解决方案,不确定是否是最佳模式:

  1. Startup.cs的机会ConfigureServices返回IServiceProvider而不是void
  2. 使用var serviceProvider = services.BuildServiceProvider();
  3. 构建serviceProvider
  4. 获取MyDbContext的实例:var context = serviceProvider.GetRequiredService<MyDbContext>();
  5. 创建QueueProcessor的实例,并将context传递给构造函数和invoke方法以开始处理消息。
  6. ConfigureServices返回新的serviceProvider

完整代码块:

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<MyDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("...")));

        var serviceProvider = services.BuildServiceProvider();

        var localMyDbContext = serviceProvider.GetRequiredService<MyDbContext>();

        Processor = new QueueProcessor(localDbContext, Configuration.GetConnectionString("Other"));
        Processor.RunAsync().GetAwaiter().GetResult();

        return serviceProvider;
    }