c# - method - No se puede resolver el servicio con ámbito desde el proveedor raíz.Net Core 2
inyeccion de dependencias.net core (3)
Middleware siempre es un singleton, por lo que no puede tener dependencias de ámbito como dependencias de constructor en el constructor de su middleware.
Middleware admite la inyección de métodos en el método Invoke, por lo que solo puede agregar IEmailRepository emailRepository como parámetro a ese método y se inyectará allí y estará bien como ámbito.
public async Task Invoke(HttpContext context, IEmailRepository emailRepository)
{
....
}
Cuando intento ejecutar mi aplicación me sale 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 puedo decir que en todos mis otros repositorios, sin embargo, no se produce ningún error para ellos. El error solo ocurre si trato de usar la aplicación.UseEmailingExceptionHandling (); línea. Aquí está algo de mi archivo 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 depósito 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 la excepción que maneja el 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>();
}
}
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 desde 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.
Otra forma de obtener la instancia de la dependencia del ámbito es inyectar el proveedor de servicios ( IServiceProvider
) en el constructor de middleware, crear el scope
en el método Invoke
y luego obtener el servicio requerido del alcance:
using (var scope = _serviceProvider.CreateScope()) {
var _emailRepository = scope.ServiceProvider.GetRequiredService<IEmailRepository>);
//do your stuff....
}
Consulte los Servicios de resolución en un cuerpo de métodos en trucos de consejos de mejores prácticas de inyección de dependencia de asp.net para obtener más detalles.
Registró el IEmailRepository
como un servicio con ámbito, en la clase de Startup
. Esto significa que no puede inyectarlo como un parámetro de constructor en Middleware
porque solo los servicios de Singleton
se pueden resolver mediante la inyección de constructor en Middleware
. Debes mover la dependencia al método Invoke
así:
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);
}
}