Kiedy próbuję uruchomić aplikację, pojawia się błąd
InvalidOperationException: Cannot resolve 'API.Domain.Data.Repositories.IEmailRepository' from root provider because it requires scoped service 'API.Domain.Data.EmailRouterContext'.
Dziwne jest to, że to EmailRepository i interfejs są skonfigurowane dokładnie tak samo, o ile wiem, jak wszystkie moje inne repozytoria, ale nie jest dla nich zgłaszany żaden błąd. Błąd występuje tylko wtedy, gdy próbuję użyć app.UseEmailingExceptionHandling (); linia. Oto część mojego pliku 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();
}
}
Oto 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;
}
}
}
I wreszcie oprogramowanie pośredniczące do obsługi wyjątków
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>();
}
}
Aktualizacja: znalazłem ten link https://github.com/aspnet/DependencyInjection/issues/578, który doprowadził mnie do zmiany metody BuildWebHost mojego pliku Program.cs z tego
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
do tego
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseDefaultServiceProvider(options =>
options.ValidateScopes = false)
.Build();
}
Nie wiem, co dokładnie się dzieje, ale teraz wydaje się, że działa.
c#
asp.net-core
asp.net-core-2.0
geoff swartz
źródło
źródło
Odpowiedzi:
Zarejestrowałeś
IEmailRepository
usługę jako usługę o określonym zakresie wStartup
klasie. Oznacza to, że nie można go wstrzyknąć jako parametru konstruktora,Middleware
ponieważ tylkoSingleton
usługi można rozpoznać przez wstrzyknięcie konstruktora w programieMiddleware
. Powinieneś przenieść zależność doInvoke
metody takiej jak ta: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); } }
źródło
Invoke
metody oprogramowania pośredniego . Możesz jednak osiągnąć coś podobnego za pośrednictwem biblioteki IoC autofac i wstrzykiwania właściwości. Zobacz wstrzykiwanie zależności ASP.NET Core MVC za pośrednictwem właściwości lub metody ustawiającej? .Innym sposobem, aby uzyskać instancję scoped zależność jest wstrzykiwać operatora (
IServiceProvider
) do middleware konstruktora, tworzyćscope
wInvoke
metodzie, a następnie uzyskać żądaną usługę z zakresu:using (var scope = _serviceProvider.CreateScope()) { var _emailRepository = scope.ServiceProvider.GetRequiredService<IEmailRepository>(); //do your stuff.... }
Zapoznaj się z usługami rozwiązywania problemów w treści metody w poradach dotyczących najlepszych praktyk iniekcji zależności asp.net core, aby uzyskać więcej informacji.
źródło
scope.ServiceProvider
zamiast_serviceProvider
na drugiej linii. Dzięki za to.IServiceScopeFactory
do tego celuOprogramowanie pośredniczące jest zawsze singletonem, więc nie można mieć zależności w zakresie jako zależności konstruktora w konstruktorze oprogramowania pośredniego.
Oprogramowanie pośredniczące obsługuje iniekcję metody w metodzie Invoke, więc możesz po prostu dodać IEmailRepository emailRepository jako parametr do tej metody i zostanie tam wstrzyknięty i będzie dobrze zgodny z zakresem.
public async Task Invoke(HttpContext context, IEmailRepository emailRepository) { .... }
źródło
Twój
middleware
iservice
musi być ze sobą kompatybilny, aby wstrzyknąćservice
przezconstructor
twójmiddleware
. Tutaj Twójmiddleware
został stworzony jako a,convention-based middleware
co oznacza, że działa jako a,singleton service
a Ty utworzyłeś swoją usługę jakoscoped-service
. Nie możesz więc wstrzyknąć ascoped-service
do konstruktora a,singleton-service
ponieważ zmusza toscoped-service
do działania jakosingleton
jeden. Oto jednak opcje.InvokeAsync
metody.middleware
wfactory-based
jeden.A
Factory-based middleware
może działać jakoscoped-service
. Możesz więc wstrzyknąć innyscoped-service
za pośrednictwem konstruktora tego oprogramowania pośredniego. Poniżej pokazałem, jak utworzyćfactory-based
oprogramowanie pośredniczące.To jest tylko do demonstracji. Więc usunąłem cały inny kod.
public class Startup { public Startup() { } public void ConfigureServices(IServiceCollection services) { services.AddScoped<TestMiddleware>(); services.AddScoped<TestService>(); } public void Configure(IApplicationBuilder app) { app.UseMiddleware<TestMiddleware>(); } }
The
TestMiddleware
:public class TestMiddleware : IMiddleware { public TestMiddleware(TestService testService) { } public Task InvokeAsync(HttpContext context, RequestDelegate next) { return next.Invoke(context); } }
The
TestService
:public class TestService { }
źródło