web-dev-qa-db-ja.com

ASP.NET Core 2の依存性注入は例外をスローします

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

ドキュメントの引用

スタートアップで利用可能なサービス

ASP.NET Core依存性注入は、アプリケーションの起動時にアプリケーションサービスを提供します。 StartupクラスのコンストラクターまたはConfigureまたはConfigureServicesメソッドのいずれかのパラメーターとして適切なインターフェースを含めることにより、これらのサービスを要求できます。

Startupクラスの各メソッドが呼び出された順に見ると、次のサービスがパラメーターとして要求される場合があります。

  • コンストラクター内:IHostingEnvironmentILoggerFactory
  • ConfigureServicesメソッド:IServiceCollection
  • Configureメソッド:IApplicationBuilderIHostingEnvironmentILoggerFactoryIApplicationLifetime

起動時に利用できないサービスを解決しようとしていますが、

...CommunicatorContext dbContext, ILdapService ldapService) {

エラーが発生します。実装にアクセスする必要がある場合は、次のいずれかを実行する必要があります。

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

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で機能します。RTMで、Configureメソッド https://github.com/aspnet/ Hosting/pull/1106

30

ASP.NET Core 2.0以降では、最初にしようとしたように、必要なスコープ付きサービスをConfigureコンストラクターに単純に挿入できます。

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

#1106 の改善により、これは非常に簡単になりました。

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

program_csの.UseStartup<Startup>()の後に追加します


私のために働く

ドキュメントはこちら

17
BriM

または、Configureメソッド内にサービススコープを作成できます。

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

Slackで述べたように、これをしないでください;-)

5
Matthew Abbott