web-dev-qa-db-fra.com

L'injection de dépendance dans ASP.NET Core 2 lève une exception

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);
}
38
M. Galczynski

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éthodes Configure ou ConfigureServices.

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:

  1. 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();
    }
    
  2. 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);
    }
    
64
Nkosi

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 .

30
Krzysztof Branicki

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 .

25
Nate Barbettini
.UseDefaultServiceProvider(options => 
            options.ValidateScopes = false)

ajoutez ceci dans Program.cs après .UseStartup<Startup>()


Travaille pour moi

Documentation ici

17
BriM

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 ;-)

5
Matthew Abbott