Cuando intento ejecutar mi aplicación, aparece el error
InvalidOperationException: Cannot resolve 'API.Domain.Data.Repositories.IEmailRepository' from root provider because it requires scoped service 'API.Domain.Data.EmailRouterContext'.
Lo que es extraño es que este EmailRepository y la interfaz están configurados exactamente de la misma manera que todos mis otros repositorios, pero no se arroja ningún error para ellos. El error solo ocurre si trato de usar la aplicación.UseEmailingExceptionHandling (); línea. Aquí hay algunos de mis archivos 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();
}
}
Aquí está el repositorio de correo electrónico
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;
}
}
}
Y finalmente el middleware de manejo de excepciones
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>();
}
}
Actualización: encontré este enlace https://github.com/aspnet/DependencyInjection/issues/578 que me llevó a cambiar el método BuildWebHost de mi archivo Program.cs de este
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
a esto
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseDefaultServiceProvider(options =>
options.ValidateScopes = false)
.Build();
}
No sé qué está pasando exactamente, pero parece funcionar ahora.
c#
asp.net-core
asp.net-core-2.0
Geoff Swartz
fuente
fuente
Respuestas:
Registraste el
IEmailRepository
como un servicio de ámbito, en laStartup
clase. Esto significa que no puede inyectarlo como un parámetro de constructor enMiddleware
porque solo losSingleton
servicios se pueden resolver mediante la inyección de constructor enMiddleware
. Debes mover la dependencia alInvoke
método de esta manera: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); } }
fuente
Invoke
método del middleware . Sin embargo, puede lograr algo similar a través de autofac IoC lib y la inyección de propiedades. Consulte ¿ Inyección de dependencia de ASP.NET Core MVC a través de la propiedad o el método de establecimiento? .Otra forma de obtener la instancia de dependencia de ámbito es inyectar el proveedor de servicios (
IServiceProvider
) en el constructor de middleware, crearscope
en elInvoke
método y luego obtener el servicio requerido del ámbito:using (var scope = _serviceProvider.CreateScope()) { var _emailRepository = scope.ServiceProvider.GetRequiredService<IEmailRepository>(); //do your stuff.... }
Consulte Resolver servicios en un cuerpo de método en asp.net, trucos de consejos de mejores prácticas de inyección de dependencia principal para obtener más detalles.
fuente
scope.ServiceProvider
lugar de hacerlo_serviceProvider
en la segunda línea. Gracias por esto.IServiceScopeFactory
para este propósitoEl middleware siempre es un singleton, por lo que no puede tener dependencias de ámbito como dependencias de constructor en el constructor de su middleware.
El middleware admite la inyección de métodos en el método Invoke, por lo que puede agregar IEmailRepository emailRepository como parámetro a ese método y se inyectará allí y estará bien según el alcance.
public async Task Invoke(HttpContext context, IEmailRepository emailRepository) { .... }
fuente
Your
middleware
y theservice
tienen que ser compatibles entre sí para poder inyectar laservice
víaconstructor
de sumiddleware
. Aquí, tumiddleware
se ha creado como, loconvention-based middleware
que significa que actúa como unsingleton service
y tú has creado tu servicio comoscoped-service
. Por lo tanto, no puede inyectar ascoped-service
en el constructor de asingleton-service
porque obligascoped-service
a que actúe comosingleton
uno. Sin embargo, estas son sus opciones.InvokeAsync
método.middleware
enfactory-based
uno.A
Factory-based middleware
es capaz de actuar comoscoped-service
. Entonces, puede inyectar otro ascoped-service
través del constructor de ese middleware. A continuación, le he mostrado cómo crear unfactory-based
middleware.Esto es solo para demostración. Entonces, he eliminado todo el otro código.
public class Startup { public Startup() { } public void ConfigureServices(IServiceCollection services) { services.AddScoped<TestMiddleware>(); services.AddScoped<TestService>(); } public void Configure(IApplicationBuilder app) { app.UseMiddleware<TestMiddleware>(); } }
El
TestMiddleware
:public class TestMiddleware : IMiddleware { public TestMiddleware(TestService testService) { } public Task InvokeAsync(HttpContext context, RequestDelegate next) { return next.Invoke(context); } }
El
TestService
:public class TestService { }
fuente