wake-up-neo.com

Abhängigkeitsinjektion in ASP.NET Core 2 löst eine Ausnahme aus

Ich erhalte die folgende Ausnahme, wenn ich versuche, benutzerdefinierten DbContext in der Configure -Methode in der Datei Startup.cs Zu verwenden. Ich verwende ASP.NET Core in Version 2.0.0-preview1-005977

Nicht behandelte Ausnahme: System.Exception: Ein Dienst des Typs 'Communicator.Backend.Data.CommunicatorContext' für den Parameter 'dbContext' der Methode 'Configure' konnte für den Typ 'Communicator.Backend.Startup' nicht aufgelöst werden. ---> System.InvalidOperationException: Der Bereichsdienst 'Communicator.Backend.Data.CommunicatorContext' vom Stammanbieter kann nicht aufgelöst werden.

Diese Ausnahme wird auch ausgelöst, wenn ich versuche, eine andere Instanz zu empfangen.

Nicht behandelte Ausnahme: System.Exception: Ein Dienst vom Typ 'Communicator.Backend.Services.ILdapService' konnte nicht aufgelöst werden ...

Hier sind meine Methoden ConfigureServices und 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

Angebotsunterlagen

In Startup verfügbare Dienste

ASP.NET Core Dependency Injection stellt Anwendungsdienste während des Starts einer Anwendung bereit. Sie können diese Services anfordern, indem Sie die entsprechende Schnittstelle als Parameter in den Konstruktor Ihrer Klasse Startup oder in eine ihrer Methoden Configure oder ConfigureServices aufnehmen.

Bei Betrachtung jeder Methode in der Klasse Startup in der Reihenfolge, in der sie aufgerufen werden, können die folgenden Dienste als Parameter angefordert werden:

  • Im Konstruktor: IHostingEnvironment, ILoggerFactory
  • In der ConfigureServices Methode: IServiceCollection
  • In der Configure Methode: IApplicationBuilder, IHostingEnvironment, ILoggerFactory, IApplicationLifetime

Sie versuchen, Dienste aufzulösen, die während des Startvorgangs nicht verfügbar sind.

...CommunicatorContext dbContext, ILdapService ldapService) {

das gibt Ihnen die Fehler, die Sie bekommen. Wenn Sie Zugriff auf die Implementierungen benötigen, müssen Sie eine der folgenden Aktionen ausführen:

  1. Ändern Sie die Methode ConfigureServices, und greifen Sie dort über die Servicesammlung auf sie zu. d.h.

    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. Ändern Sie die Methode ConfigureServices, um IServiceProvider zurückzugeben, und die Methode Configure, um ein IServiceProvider zu übernehmen und Ihre Abhängigkeiten dort aufzulösen. d.h.

    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

Die Lösung von NKosi funktioniert, weil Sie durch Aufrufen von services.BuildServiceProvider() ohne Parameter das validateScopes nicht übergeben. Da diese Überprüfung deaktiviert ist, wird die Ausnahme nicht ausgelöst. Dies bedeutet nicht, dass das Problem nicht vorhanden ist.

EF Core DbContext ist mit einem bestimmten Lebensstil registriert. In ASP native DI ist der Container-Bereich mit der Instanz von IServiceProvider verbunden. Wenn Sie Ihr DbContext normalerweise von einem Controller aus verwenden, gibt es kein Problem, weil = ASP) erstellt für jede Anforderung einen neuen Bereich (neue Instanz von IServiceProvider) und löst dann alles in dieser Anforderung auf. Während des Anwendungsstarts ist jedoch kein Anforderungsbereich vorhanden Sie haben eine Instanz von IServiceProvider, die nicht über einen Gültigkeitsbereich verfügt (oder mit anderen Worten im Stammbereich). Dies bedeutet, dass Sie einen Gültigkeitsbereich selbst erstellen sollten. Sie können dies folgendermaßen tun:

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
}

Die Methode ConfigureServices kann unverändert bleiben.

[~ # ~] edit [~ # ~]

Ihre Lösung wird in 2.0.0 RTM ohne Änderungen funktionieren, da in RTM ein Dienstanbieter mit Gültigkeitsbereich für die Methode Configure erstellt wird https: // github.com/aspnet/Hosting/pull/1106 .

30

In ASP.NET Core 2.0 und neueren Versionen können Sie den benötigten Bereichsdienst einfach in den Konstruktor Configure einfügen, wie Sie es ursprünglich versucht haben:

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

Dies ist dank der Verbesserungen in # 1106 viel einfacher.

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

füge dies in Program.cs nach .UseStartup<Startup>() hinzu


Funktioniert bei mir

Dokumentation hier

17
BriM

Alternativ können Sie einen Servicebereich innerhalb Ihrer Configure -Methode erstellen:

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

Obwohl, wie bei Slack erwähnt, mach das nicht ;-)

5
Matthew Abbott