Wenn ich versuche, meine App auszuführen, wird der Fehler angezeigt
InvalidOperationException: Cannot resolve 'API.Domain.Data.Repositories.IEmailRepository' from root provider because it requires scoped service 'API.Domain.Data.EmailRouterContext'.
Was seltsam ist, ist, dass dieses EmailRepository und diese Schnittstelle, soweit ich das beurteilen kann, genau so eingerichtet sind wie alle meine anderen Repositorys, aber für sie wird kein Fehler ausgegeben. Der Fehler tritt nur auf, wenn ich versuche, die App zu verwenden.UseEmailingExceptionHandling (); Linie. Hier sind einige meiner Startup.cs-Dateien.
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();
}
}
Hier ist das 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;
}
}
}
Und schließlich die Ausnahmebehandlung für Middleware
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>();
}
}
Update: Ich habe diesen Link https://github.com/aspnet/DependencyInjection/issues/578 gefunden, wodurch ich die BuildWebHost-Methode meiner Program.cs-Datei geändert habe
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
dazu
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseDefaultServiceProvider(options =>
options.ValidateScopes = false)
.Build();
}
Ich weiß nicht genau, was los ist, aber es scheint jetzt zu funktionieren.
quelle
Antworten:
Sie haben den
IEmailRepository
als Scoped Service in derStartup
Klasse registriert . Dies bedeutet, dass Sie es nicht als Konstruktorparameter einfügen können,Middleware
da nurSingleton
Dienste durch Einfügen eines Konstruktors in aufgelöst werden könnenMiddleware
. Sie sollten die AbhängigkeitInvoke
wie folgt auf die Methode verschieben: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); } }
quelle
Invoke
Methode. Sie können jedoch etwas Ähnliches über die Autofac-IoC-Bibliothek und die Eigenschaftsinjektion erreichen. Siehe ASP.NET Core MVC-Abhängigkeitsinjektion über Eigenschaft oder Setter-Methode? .Eine weitere Möglichkeit , die Instanz von scoped Abhängigkeit zu erhalten , ist Service - Provider (zu injizieren
IServiceProvider
) in den Middleware - Konstruktor erstellenscope
inInvoke
Methode und dann den gewünschten Dienst aus dem Anwendungsbereich erhalten:using (var scope = _serviceProvider.CreateScope()) { var _emailRepository = scope.ServiceProvider.GetRequiredService<IEmailRepository>(); //do your stuff.... }
Schauen Sie sich Resolving Dienstleistungen in einem Verfahren Körper in asp.net Kern Dependency Injection Best Practices Tipps Tricks für weitere Details.
quelle
scope.ServiceProvider
anstatt_serviceProvider
in der zweiten Zeile. Danke dafür.IServiceScopeFactory
für diesen Zweck zu verwendenMiddleware ist immer ein Singleton, sodass Sie im Konstruktor Ihrer Middleware keine Abhängigkeiten als Konstruktorabhängigkeiten festlegen können.
Middleware unterstützt die Methodeninjektion für die Invoke-Methode. Sie können also einfach das IEmailRepository emailRepository als Parameter zu dieser Methode hinzufügen. Es wird dort injiziert und ist in Ordnung.
public async Task Invoke(HttpContext context, IEmailRepository emailRepository) { .... }
quelle
Ihr
middleware
und dasservice
müssen miteinander kompatibel sein, um dasservice
über dasconstructor
von Ihnen injizieren zu könnenmiddleware
. Hier wurde Ihrmiddleware
als erstellt,convention-based middleware
was bedeutet, dass es als fungiertsingleton service
und Sie Ihren Dienst als erstellt habenscoped-service
. Sie können also kein ascoped-service
in den Konstruktor von a injizieren,singleton-service
weil es das zwingtscoped-service
, alssingleton
Eins zu agieren . Hier sind jedoch Ihre Optionen.InvokeAsync
Methode ein.middleware
in einefactory-based
Eins.A
Factory-based middleware
kann alsscoped-service
. Sie können also eine anderescoped-service
über den Konstruktor dieser Middleware einfügen. Im Folgenden habe ich Ihnen gezeigt, wie Sie einefactory-based
Middleware erstellen .Dies ist nur zur Demonstration. Also habe ich den ganzen anderen Code entfernt.
public class Startup { public Startup() { } public void ConfigureServices(IServiceCollection services) { services.AddScoped<TestMiddleware>(); services.AddScoped<TestService>(); } public void Configure(IApplicationBuilder app) { app.UseMiddleware<TestMiddleware>(); } }
Die
TestMiddleware
:public class TestMiddleware : IMiddleware { public TestMiddleware(TestService testService) { } public Task InvokeAsync(HttpContext context, RequestDelegate next) { return next.Invoke(context); } }
Die
TestService
:public class TestService { }
quelle