核心 2 中的依赖注入 ASP.NET 引发异常



当我尝试在文件中Configure方法中使用自定义 DbContext 时Startup.cs我收到以下异常。我在版本 2.0.0-preview1-005977 中使用 ASP.NET Core

未处理的异常:System.Exception:无法解析类型为"Communicator.Backend.Data.CommunicatorContext"类型的服务,用于类型"Communicator.Backend.Startup"上方法"配置"的参数"dbContext"。 ---> System.InvalidOperationException:无法从根提供程序解析作用域服务"Communicator.Backend.Data.CommunicatorContext"。

如果我尝试接收其他实例时,也会抛出此异常。

未处理的异常:系统异常:无法解析类型为"Communicator.Backend.Services.ILdapService"的服务...

以下是我的ConfigureServicesConfigure方法。

public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<CommunicatorContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddCookieAuthentication();
services.Configure<LdapConfig>(Configuration.GetSection("Ldap"));
services.AddScoped<ILdapService, LdapService>();
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, CommunicatorContext dbContext, ILdapService ldapService)
{
app.UseAuthentication();
app.UseWebSockets();
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")
{
if (context.WebSockets.IsWebSocketRequest)
{
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Echo(context, webSocket);
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next();
}
});
app.UseMvc();
DbInitializer.Initialize(dbContext, ldapService);
}

引用文档

启动中可用的服务

ASP.NET 核心依赖注入在 应用程序的启动。您可以通过以下方式请求这些服务 适当的接口作为Startup类的参数 构造函数或其ConfigureConfigureServices方法之一。

按以下顺序查看Startup类中的每个方法 它们被调用,以下服务可能会被请求为 参数:

  • 在构造函数中:IHostingEnvironmentILoggerFactory
  • ConfigureServices方法中:IServiceCollection
  • Configure方法中:IApplicationBuilderIHostingEnvironmentILoggerFactoryIApplicationLifetime

您正在尝试解析在启动期间不可用的服务,

...CommunicatorContext dbContext, ILdapService ldapService) {

这将为您提供遇到的错误。如果您需要访问实现,则需要执行以下操作之一:

  1. 修改ConfigureServices方法并从服务集合访问它们。

    public IServiceProvider ConfigureServices(IServiceCollection services) {
    services.AddDbContext<CommunicatorContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    services.AddCookieAuthentication();
    services.Configure<LdapConfig>(Configuration.GetSection("Ldap"));
    services.AddScoped<ILdapService, LdapService>();
    services.AddMvc();
    // Build the intermediate service provider
    var serviceProvider = services.BuildServiceProvider();
    //resolve implementations
    var dbContext = serviceProvider.GetService<CommunicatorContext>();
    var ldapService = serviceProvider.GetService<ILdapService>();
    DbInitializer.Initialize(dbContext, ldapService);
    //return the provider
    return serviceProvider();
    }
    
  2. 修改ConfigureServices方法以返回 IServiceProvider,Configure方法获取IServiceProvider,然后在那里解析依赖项。

    public IServiceProvider ConfigureServices(IServiceCollection services) {
    services.AddDbContext<CommunicatorContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    services.AddCookieAuthentication();
    services.Configure<LdapConfig>(Configuration.GetSection("Ldap"));
    services.AddScoped<ILdapService, LdapService>();
    services.AddMvc();
    // Build the intermediate service provider then return it
    return services.BuildServiceProvider();
    }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, 
    ILoggerFactory loggerFactory, IServiceProvider serviceProvider) {
    //...Other code removed for brevity
    app.UseMvc();
    //resolve dependencies
    var dbContext = serviceProvider.GetService<CommunicatorContext>();
    var ldapService = serviceProvider.GetService<ILdapService>();
    DbInitializer.Initialize(dbContext, ldapService);
    }
    

NKosi 的解决方案之所以有效,是因为在没有参数的情况下调用services.BuildServiceProvider()自己,您不会传递validateScopes。由于禁用了此验证,因此不会引发异常。这并不意味着问题不存在。

EF CoreDbContext已注册为限定范围的生活方式。在 ASP 本机 DI 中,容器作用域连接到IServiceProvider的实例。通常,当您从控制器使用DbContext时,没有问题,因为ASP为每个请求创建一个新的作用域(IServiceProvider的新实例),然后使用它来解决此请求中的所有内容。但是,在应用程序启动期间,您没有请求范围。您有一个未限定范围的IServiceProvider实例(换句话说,在根作用域中)。这意味着您应该自己创建一个范围。你可以这样做:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
var scopeFactory = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<CommunicatorContext>();
var ldapService = scope.ServiceProvider.GetRequiredService<ILdapService>();
// rest of your code
}
// rest of Configure setup
}

ConfigureServices方法可以保持不变。

编辑

您的解决方案将在 2.0.0 RTM 中工作,无需任何更改,因为在 RTM 中将为配置方法 https://github.com/aspnet/Hosting/pull/1106 创建范围的服务提供程序。

在 Core 2.0 及更高版本中 ASP.NET 您可以简单地将所需的作用域服务注入Configure构造函数中,就像您最初尝试的那样:

public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
CommunicatorContext dbContext,
ILdapService ldapService)
{
// ...
}

由于 #1106 中的改进,这要容易得多。

.UseDefaultServiceProvider(options => 
options.ValidateScopes = false)

.UseStartup<Startup>()后将其添加到程序中.cs


为我工作

文档在这里

或者,您可以在Configure方法中创建服务范围:

var scopeFactory = ApplicationServices.GetService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetService<CommunicatorDbContext>();
DbInitializer.Initializer(dbContext, ldapService);
}

虽然,正如Slack上提到的,不要这样做;-)

最新更新