c# - services - La inyección de dependencia en ASP.NET Core 2 lanza una excepción
iservicecollection (5)
Alternativamente, puede crear un alcance de servicio dentro de su método de Configure
:
var scopeFactory = ApplicationServices.GetService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetService<CommunicatorDbContext>();
DbInitializer.Initializer(dbContext, ldapService);
}
Aunque, como se menciona en Slack, no hagas esto ;-)
Recibo la siguiente excepción cuando trato de usar DbContext personalizado en el método Configure
en el archivo Startup.cs
. Uso ASP.NET Core en la versión 2.0.0-preview1-005977
Excepción no controlada: System.Exception: No se pudo resolver un servicio de tipo ''Communicator.Backend.Data.CommunicatorContext'' para el parámetro ''dbContext'' del método ''Configurar'' en el tipo ''Communicator.Backend.Startup''. ---> System.InvalidOperationException: No se puede resolver el servicio con ámbito ''Communicator.Backend.Data.CommunicatorContext'' del proveedor raíz.
Esta excepción también se produce en caso de que intente recibir otra instancia.
Excepción no controlada: System.Exception: no se pudo resolver un servicio del tipo ''Communicator.Backend.Services.ILdapService''
...
Aquí están mis métodos ConfigureServices
y 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);
}
Citando documentación
Servicios disponibles en Startup
La inyección de dependencia de ASP.NET Core proporciona servicios de aplicación durante el inicio de una aplicación. Puede solicitar estos servicios al incluir la interfaz apropiada como un parámetro en el constructor de su clase de
Startup
o uno de sus métodosConfigure
oConfigureServices
.Al examinar cada método en la clase de
Startup
en el orden en que se llaman, se pueden solicitar los siguientes servicios como parámetros:
- En el constructor:
IHostingEnvironment
,ILoggerFactory
- En el método
ConfigureServices
:IServiceCollection
- En el método de
Configure
:IApplicationBuilder
,IHostingEnvironment
,ILoggerFactory
,IApplicationLifetime
Está intentando resolver servicios que no están disponibles durante el inicio,
...CommunicatorContext dbContext, ILdapService ldapService) {
Lo que le dará los errores que está recibiendo. Si necesita acceder a las implementaciones, debe realizar una de las siguientes acciones:
Modifique el método
ConfigureServices
y acceda a ellos desde la colección de servicios. es decirpublic 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(); }
Modifique el método
ConfigureServices
para devolver IServiceProvider,Configure
método para tomar unIServiceProvider
y luego resuelva sus dependencias allí. es decirpublic 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); }
En ASP.NET Core 2.0 y versiones más recientes, simplemente puede inyectar el servicio con ámbito que necesita en el constructor de Configure
, como intentó hacerlo inicialmente:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
CommunicatorContext dbContext,
ILdapService ldapService)
{
// ...
}
Esto es mucho más fácil, gracias a las mejoras en https://github.com/aspnet/Hosting/pull/1106 .
La solución de NKosi funciona porque al invocar services.BuildServiceProvider()
usted mismo sin parámetros, no está pasando los validateScopes
. Debido a que esta validación está deshabilitada, la excepción no se lanza. Esto no significa, sin embargo, el problema no está allí.
EF Core DbContext
está registrado con un estilo de vida de ámbito. En ASP, el contenedor DI nativo está conectado a la instancia de IServiceProvider
. Normalmente, cuando usa su DbContext
desde el Controlador, no hay problema porque ASP crea un nuevo ámbito (nueva instancia de IServiceProvider
) para cada solicitud y luego lo usa para resolver todo lo que hay dentro de esta solicitud. Sin embargo, durante el inicio de la aplicación no tiene un alcance de solicitud. Tiene una instancia de IServiceProvider
que no está dentro del alcance (o en otras palabras, en el ámbito raíz). Esto significa que usted debe crear alcance usted mismo. Puedes hacerlo así:
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
}
El método ConfigureServices
puede permanecer sin cambios.
EDITAR
Su solución funcionará en 2.0.0 RTM sin ningún cambio, ya que en RTM, se creará un proveedor de servicios para el método de configuración https://github.com/aspnet/Hosting/pull/1106 .
.UseDefaultServiceProvider(options =>
options.ValidateScopes = false)
agregue esto en Program.cs después de .UseStartup<Startup>()
Funciona para mi