Когда я пытаюсь запустить свое приложение, я получаю сообщение об ошибке
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();
}
}
Вот репозиторий электронной почты
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 = "errors@ourdomain.com";
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();
}
Я не знаю, что именно происходит, но, похоже, сейчас это работает.
c#
asp.net-core
asp.net-core-2.0
Джефф Шварц
источник
источник
Ответы:
Вы зарегистрировали
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); } }
источник
Invoke
. Однако вы можете добиться чего-то подобного с помощью autofac IoC lib и инъекции свойств. Смотрите ASP.NET Core MVC Dependency Injection через свойство или метод установки? .Другой способ получить экземпляр зависимости с ограниченной областью видимости - это вставить service provider (
IServiceProvider
) в конструктор промежуточного программного обеспечения, создать методscope
inInvoke
и затем получить требуемую услугу из области:using (var scope = _serviceProvider.CreateScope()) { var _emailRepository = scope.ServiceProvider.GetRequiredService<IEmailRepository>(); //do your stuff.... }
Дополнительные сведения см. В разделе «Разрешение служб в теле метода» в разделе «Рекомендации по внедрению зависимостей ядра asp.net» .
источник
scope.ServiceProvider
вместо_serviceProvider
второй строки. Спасибо за это.IServiceScopeFactory
для этой целиПромежуточное ПО всегда является одноэлементным, поэтому у вас не может быть зависимостей в виде зависимостей конструктора в конструкторе вашего промежуточного программного обеспечения.
Промежуточное ПО поддерживает внедрение метода в метод Invoke, поэтому вы можете просто добавить IEmailRepository emailRepository в качестве параметра к этому методу, и он будет внедрен туда и будет работать в соответствии с областью действия.
public async Task Invoke(HttpContext context, IEmailRepository emailRepository) { .... }
источник
Ваш
middleware
иservice
он должны быть совместимы друг с другом, чтобы вводитьservice
черезconstructor
вашmiddleware
. Вот твойmiddleware
был создан как,convention-based middleware
что означает, что он действует как,singleton service
а вы создали свою службу какscoped-service
. Итак, вы не можете вставить ascoped-service
в конструктор a,singleton-service
потому что он заставляетscoped-service
его действовать какsingleton
единицу. Однако вот ваши варианты.InvokeAsync
метода.middleware
вfactory-based
одного.А
Factory-based middleware
может действовать какscoped-service
. Итак, вы можете внедрить еще одинscoped-service
через конструктор этого промежуточного программного обеспечения. Ниже я показал вам, как создатьfactory-based
промежуточное ПО.Это только для демонстрации. Итак, я удалил весь остальной код.
public class Startup { public Startup() { } public void ConfigureServices(IServiceCollection services) { services.AddScoped<TestMiddleware>(); services.AddScoped<TestService>(); } public void Configure(IApplicationBuilder app) { app.UseMiddleware<TestMiddleware>(); } }
В
TestMiddleware
:public class TestMiddleware : IMiddleware { public TestMiddleware(TestService testService) { } public Task InvokeAsync(HttpContext context, RequestDelegate next) { return next.Invoke(context); } }
В
TestService
:public class TestService { }
источник