ASP.NET Core 2 中的依赖注入引发异常
Posted
技术标签:
【中文标题】ASP.NET Core 2 中的依赖注入引发异常【英文标题】:Dependency injection in ASP.NET Core 2 throws exception 【发布时间】:2017-10-26 02:52:30 【问题描述】:当我尝试在 Startup.cs
文件中的 Configure
方法中使用自定义 DbContext 时收到以下异常。我在 2.0.0-preview1-005977 版本中使用 ASP.NET Core
未处理的异常:System.Exception:无法为“Communicator.Backend.Startup”类型的方法“Configure”的参数“dbContext”解析“Communicator.Backend.Data.CommunicatorContext”类型的服务。 ---> System.InvalidOperationException:无法从根提供程序解析范围服务“Communicator.Backend.Data.CommunicatorContext”。
如果我尝试接收其他实例,也会引发此异常。
未处理的异常:System.Exception:无法解析“Communicator.Backend.Services.ILdapService”类型的服务
...
这是我的 ConfigureServices
和 Configure
方法。
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);
【问题讨论】:
【参考方案1】:引用文档
Services Available in Startup
ASP.NET Core 依赖注入提供应用服务 应用程序的启动。您可以通过包括以下内容来请求这些服务 适当的接口作为
Startup
类的参数 构造函数或其Configure
或ConfigureServices
方法之一。按顺序查看
在构造函数中:Startup
类中的每个方法 它们被调用时,可能会请求以下服务 参数:IHostingEnvironment
,ILoggerFactory
在ConfigureServices
方法中:IServiceCollection
在Configure
方法中:IApplicationBuilder
,IHostingEnvironment
,ILoggerFactory
,IApplicationLifetime
您正在尝试解决启动期间不可用的服务,
...CommunicatorContext dbContext, ILdapService ldapService)
这会给你你得到的错误。如果您需要访问实现,则需要执行以下操作之一:
修改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();
修改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);
【讨论】:
谢谢,这解决了我的问题。如果我可以添加更多,在测试了两个选项后,我发现我仍然需要将ConfigureServices
从返回void
(默认)修改为返回IServiceProvider
,以便第二个选项起作用。
遇到了类似的问题。让ConfigureServices()
单独返回IServiceProvider
使得通过IApplicationBuilder.ApplicationServices.GetService<T>()
解决我的依赖关系成为可能。我不需要将IServiceProvider
注入Configure()
方法。【参考方案2】:
NKosi 的解决方案有效,因为通过自己调用services.BuildServiceProvider()
而不使用参数,您不会传递validateScopes
。由于禁用了此验证,因此不会引发异常。这并不意味着问题不存在。
EF Core DbContext
已使用范围生活方式注册。在 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 中运行而无需任何更改,因为将为配置方法 https://github.com/aspnet/Hosting/pull/1106 创建 RTM 范围内的服务提供者。
【讨论】:
【参考方案3】:在 ASP.NET Core 2.0 及更高版本中,您可以简单地将所需的作用域服务注入 Configure
构造函数,就像您最初尝试做的那样:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
CommunicatorContext dbContext,
ILdapService ldapService)
// ...
这要容易得多,感谢#1106 的改进。
【讨论】:
这是解决问题的自然方法。谢谢。 使用 2.0 RTM 的最佳方式【参考方案4】:.UseDefaultServiceProvider(options =>
options.ValidateScopes = false)
在.UseStartup<Startup>()
之后的Program.cs中添加这个
为我工作
Documentation Here
【讨论】:
谢谢。也适用于 Asp.Net Core 2.1 Api App 。 ValidateScopes 实际上在做什么?【参考方案5】:或者,您可以在 Configure
方法中创建服务范围:
var scopeFactory = ApplicationServices.GetService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
var dbContext = scope.ServiceProvider.GetService<CommunicatorDbContext>();
DbInitializer.Initializer(dbContext, ldapService);
尽管如 Slack 上所述,不要这样做 ;-)
【讨论】:
这有什么问题?为什么你建议不要这样做。我现在有这个:using (var scope = app.ApplicationServices.CreateScope()) scope.ServiceProvider.GetService<IInitDatabase>().InitAsync().Wait();
我应该以其他方式执行此操作吗?
在 Slack 上提到了什么?为什么不应该这样做? @krzysztof-branicki 的回答对此表示赞同,并且已在几个月前发布,您为什么不将其纳入您的回答中?以上是关于ASP.NET Core 2 中的依赖注入引发异常的主要内容,如果未能解决你的问题,请参考以下文章