Не удается разрешить услугу с ограниченным доступом от корневого поставщика.Net Core 2
Когда я пытаюсь запустить мое приложение, я получаю ошибку
InvalidOperationException: Cannot resolve 'API.Domain.Data.Repositories.IEmailRepository' from root provider because it requires scoped service 'API.Domain.Data.EmailRouterContext'.
Что странно в том, что этот EmailRepository и интерфейс настроены точно так же, насколько я могу сказать, как все мои другие репозитории, но для них не возникает ошибка. Ошибка возникает только в том случае, если я пытаюсь использовать app.UseEmailingExceptionHandling(); линия. Вот некоторые из моего файла Startup.cs.
public class Startup
{
public IConfiguration Configuration { get; protected set; }
private APIEnvironment _environment { get; set; }
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
Configuration = configuration;
_environment = APIEnvironment.Development;
if (env.IsProduction()) _environment = APIEnvironment.Production;
if (env.IsStaging()) _environment = APIEnvironment.Staging;
}
public void ConfigureServices(IServiceCollection services)
{
var dataConnect = new DataConnect(_environment);
services.AddDbContext<GeneralInfoContext>(opt => opt.UseSqlServer(dataConnect.GetConnectString(Database.GeneralInfo)));
services.AddDbContext<EmailRouterContext>(opt => opt.UseSqlServer(dataConnect.GetConnectString(Database.EmailRouter)));
services.AddWebEncoders();
services.AddMvc();
services.AddScoped<IGenInfoNoteRepository, GenInfoNoteRepository>();
services.AddScoped<IEventLogRepository, EventLogRepository>();
services.AddScoped<IStateRepository, StateRepository>();
services.AddScoped<IEmailRepository, EmailRepository>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
app.UseAuthentication();
app.UseStatusCodePages();
app.UseEmailingExceptionHandling();
app.UseMvcWithDefaultRoute();
}
}
Вот EmailRepository
public interface IEmailRepository
{
void SendEmail(Email email);
}
public class EmailRepository : IEmailRepository, IDisposable
{
private bool disposed;
private readonly EmailRouterContext edc;
public EmailRepository(EmailRouterContext emailRouterContext)
{
edc = emailRouterContext;
}
public void SendEmail(Email email)
{
edc.EmailMessages.Add(new EmailMessages
{
DateAdded = DateTime.Now,
FromAddress = email.FromAddress,
MailFormat = email.Format,
MessageBody = email.Body,
SubjectLine = email.Subject,
ToAddress = email.ToAddress
});
edc.SaveChanges();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
edc.Dispose();
disposed = true;
}
}
}
И, наконец, промежуточное программное обеспечение обработки исключений
public class ExceptionHandlingMiddleware
{
private const string ErrorEmailAddress = "[email protected]";
private readonly IEmailRepository _emailRepository;
private readonly RequestDelegate _next;
public ExceptionHandlingMiddleware(RequestDelegate next, IEmailRepository emailRepository)
{
_next = next;
_emailRepository = emailRepository;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next.Invoke(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex, _emailRepository);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception,
IEmailRepository emailRepository)
{
var code = HttpStatusCode.InternalServerError; // 500 if unexpected
var email = new Email
{
Body = exception.Message,
FromAddress = ErrorEmailAddress,
Subject = "API Error",
ToAddress = ErrorEmailAddress
};
emailRepository.SendEmail(email);
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int) code;
return context.Response.WriteAsync("An error occured.");
}
}
public static class AppErrorHandlingExtensions
{
public static IApplicationBuilder UseEmailingExceptionHandling(this IApplicationBuilder app)
{
if (app == null)
throw new ArgumentNullException(nameof(app));
return app.UseMiddleware<ExceptionHandlingMiddleware>();
}
}
Обновление: я нашел эту ссылку https://github.com/aspnet/DependencyInjection/issues/578, которая привела меня к изменению моего метода BuildWebHost Program.cs из этого
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
к этому
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseDefaultServiceProvider(options =>
options.ValidateScopes = false)
.Build();
}
Я не знаю, что именно происходит, но теперь это работает.
Ответы
Ответ 1
Вы зарегистрировали IEmailRepository
как сервис с определенными IEmailRepository
в классе Startup
. Это означает, что вы не можете внедрить его как параметр конструктора в Middleware
потому что только сервисы Singleton
могут быть разрешены путем внедрения конструктора в Middleware
. Вы должны переместить зависимость в метод Invoke
следующим образом:
public ExceptionHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, IEmailRepository emailRepository)
{
try
{
await _next.Invoke(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex, emailRepository);
}
}
Ответ 2
Среднее ПО всегда является одноэлементным, поэтому вы не можете иметь зависимые от контура зависимости как зависимости конструктора в конструкторе вашего промежуточного программного обеспечения.
Среднее ПО поддерживает метод инъекции по методу Invoke, поэтому вы можете просто добавить IEmailRepository emailRepository в качестве параметра к этому методу, и он будет введен там и будет в порядке, как и в области видимости.
public async Task Invoke(HttpContext context, IEmailRepository emailRepository)
{
....
}
Ответ 3
Другой способ получить экземпляр зависимостей в области действия - внедрить поставщика услуг (IServiceProvider
) в конструктор промежуточного программного обеспечения, создать scope
в методе Invoke
а затем получить требуемую службу из области:
using (var scope = _serviceProvider.CreateScope()) {
var _emailRepository = scope.ServiceProvider.GetRequiredService<IEmailRepository>);
//do your stuff....
}
Ознакомьтесь с разделом Resolving Services в теле метода в asp.net. Внедрение советов по внедрению основных зависимостей дает советы для получения дополнительной информации.