Je reçois l'exception suivante lorsque j'essaie d'utiliser DbContext personnalisé dans la méthode Configure
de Startup.cs
fichier. J'utilise ASP.NET Core dans la version 2.0.0-preview1-005977
Exception non gérée: System.Exception: impossible de résoudre un service de type 'Communicator.Backend.Data.CommunicatorContext' pour le paramètre 'dbContext' de la méthode 'Configure' sur le type 'Communicator.Backend.Startup'. ---> System.InvalidOperationException: impossible de résoudre le service défini 'Communicator.Backend.Data.CommunicatorContext' à partir du fournisseur racine.
Cette exception est également levée dans le cas où j'essaie de recevoir une autre instance.
Exception non gérée: System.Exception: impossible de résoudre un service de type 'Communicator.Backend.Services.ILdapService'
...
Voici mes méthodes ConfigureServices
et 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);
}
Citation de la documentation
Services disponibles au démarrage
L'injection de dépendance ASP.NET Core fournit des services d'application au démarrage d'une application. Vous pouvez demander ces services en incluant l'interface appropriée en tant que paramètre du constructeur de votre classe
Startup
ou de l'une de ses méthodesConfigure
ouConfigureServices
.En examinant chaque méthode de la classe
Startup
dans l'ordre dans lequel elles ont été appelées, les services suivants peuvent être demandés en tant que paramètres:
- Dans le constructeur:
IHostingEnvironment
,ILoggerFactory
- Dans la méthode
ConfigureServices
:IServiceCollection
- Dans la méthode
Configure
:IApplicationBuilder
,IHostingEnvironment
,ILoggerFactory
,IApplicationLifetime
Vous essayez de résoudre des services qui ne sont pas disponibles au démarrage,
...CommunicatorContext dbContext, ILdapService ldapService) {
ce qui vous donnera les erreurs que vous obtenez. Si vous avez besoin d'accéder aux implémentations, vous devez effectuer l'une des opérations suivantes:
Modifiez la méthode ConfigureServices
et accédez-y à partir de la collection de services. c'est à dire.
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();
}
Modifiez la méthode ConfigureServices
pour renvoyer IServiceProvider, la méthode Configure
pour prendre un IServiceProvider
et y résoudre vos dépendances. c'est à dire.
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);
}
La solution de NKosi fonctionne car en appelant services.BuildServiceProvider()
vous-même sans paramètres, vous ne transmettez pas le validateScopes
. Cette validation étant désactivée, l'exception n'est pas levée. Cela ne signifie pas que le problème n'est pas là.
EF Core DbContext
est enregistré avec un mode de vie limité. Dans ASP ID native, l'étendue du conteneur est connectée à l'instance de IServiceProvider
. Normalement, lorsque vous utilisez votre DbContext
à partir d'un contrôleur, il n'y a pas de problème, car = ASP crée une nouvelle portée (nouvelle instance de IServiceProvider
) pour chaque demande, puis l'utilise pour résoudre tout le contenu de cette demande. Toutefois, lors du démarrage de l'application, vous n'avez pas la portée de la demande. Vous avez une instance de IServiceProvider
qui n’est pas étendue (ou en d’autres termes dans la portée racine). Cela signifie que vous devez créer une portée vous-même. Vous pouvez le faire comme ceci:
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
}
La méthode ConfigureServices
peut rester inchangée.
[~ # ~] éditer [~ # ~]
Votre solution fonctionnera dans la version 2.0.0 RTM sans aucune modification, car dans RTM, le fournisseur de services défini sera créé pour la méthode Configure https: // github.com/aspnet/Hosting/pull/1106 .
Dans ASP.NET Core 2.0 et versions ultérieures, vous pouvez simplement injecter le service délimité dont vous avez besoin dans le constructeur Configure
, comme vous aviez essayé de le faire initialement:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
CommunicatorContext dbContext,
ILdapService ldapService)
{
// ...
}
C’est beaucoup plus facile grâce aux améliorations de # 1106 .
.UseDefaultServiceProvider(options =>
options.ValidateScopes = false)
ajoutez ceci dans Program.cs après .UseStartup<Startup>()
Travaille pour moi
Vous pouvez également créer une étendue de service dans votre méthode Configure
:
var scopeFactory = ApplicationServices.GetService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetService<CommunicatorDbContext>();
DbInitializer.Initializer(dbContext, ldapService);
}
Bien que, comme mentionné sur Slack, ne faites pas ceci ;-)