Подтвердить что ты не робот

Включение зависимостей в ASP.NET Core 2 выбрасывает исключение

Я получать следующее исключение при попытке использовать пользовательские DbContext в Configure метода в Startup.cs файле. Я использую ASP.NET Core в версии 2.0.0-preview1-005977

Необработанное исключение: System.Exception: не удалось разрешить службу типа 'Communicator.Backend.Data.CommunicatorContext' для параметра 'dbContext' метода 'Configure' для типа 'Communicator.Backend.Startup'. ---> 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);
}
4b9b3361

Ответ 1

Цитата документации

Службы, доступные при запуске

Вставка ASP.NET Core для зависимостей обеспечивает услуги приложений во время запуск приложения. Вы можете запросить эти услуги, включив соответствующий интерфейс в качестве параметра в вашем классе Startupконструктор или один из его методов Configure или ConfigureServices.

Рассмотрение каждого метода в классе Startup в порядке, в котором они вызываются, следующие услуги могут запрашиваться как Параметры:

  • В конструкторе: IHostingEnvironment, ILoggerFactory
  • В методе ConfigureServices: IServiceCollection
  • В методе Configure: IApplicationBuilder, IHostingEnvironment, ILoggerFactory, IApplicationLifetime

Вы пытаетесь разрешить службы, недоступные во время запуска,

...CommunicatorContext dbContext, ILdapService ldapService) {

который даст вам ошибки, которые вы получаете. Если вам нужен доступ к реализациям, вам необходимо выполнить одно из следующих действий:

  • Измените метод 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();
    }
    
  • Измените метод 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);
    }
    

Ответ 2

Решение от NKosi работает, потому что, вызывая services.BuildServiceProvider() самостоятельно без параметров, вы не передаете validateScopes. Поскольку эта проверка отключена, исключение не выдается. Это не значит, что проблема не в этом.

EF Core DbContext зарегистрирован с ограниченным образом жизни. В собственном DI ASP область контейнера связана с экземпляром 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.

Ответ 3

В ASP.NET Core 2.0 и новее вы можете просто внедрить нужную вам сервисную область в конструктор Configure, как вы пытались сделать изначально:

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

Это намного проще, благодаря улучшениям в # 1106.

Ответ 4

.UseDefaultServiceProvider(options => 
            options.ValidateScopes = false)

добавить это в Program.cs после .UseStartup<Startup>()


Работает для меня

Документация здесь

Ответ 5

В качестве альтернативы вы можете создать область обслуживания в своем методе Configure:

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

Хотя, как упоминалось в Slack, не делайте этого: -)