Lorsque j'essaie d'exécuter mon application, j'obtiens l'erreur
InvalidOperationException: Cannot resolve 'API.Domain.Data.Repositories.IEmailRepository' from root provider because it requires scoped service 'API.Domain.Data.EmailRouterContext'.
Ce qui est étrange, c’est que EmailRepository et son interface sont configurés de la même manière que tous mes autres référentiels, mais qu’aucune erreur n’a été commise. L'erreur ne survient que si j'essaie d'utiliser la méthode app.UseEmailingExceptionHandling (); ligne. Voici quelques-uns de mes fichiers 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();
}
}
Voici le 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;
}
}
}
Et enfin le middleware de gestion des exceptions
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>();
}
}
Mise à jour: j'ai trouvé ce lien https://github.com/aspnet/DependencyInjection/issues/578 ce qui m'a amené à modifier la méthode BuildWebHost de mon fichier Program.cs à partir de cette page
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
pour ça
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseDefaultServiceProvider(options =>
options.ValidateScopes = false)
.Build();
}
Je ne sais pas ce qui se passe exactement, mais cela semble fonctionner maintenant.
Vous avez enregistré le IEmailRepository
en tant que service limité, dans la classe Startup
. Cela signifie que vous ne pouvez pas l'injecter en tant que paramètre de constructeur dans Middleware
, car seuls les services Singleton
peuvent être résolus par injection de constructeur dans Middleware
. Vous devriez déplacer la dépendance vers la méthode Invoke
comme ceci:
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);
}
}
Une autre façon d'obtenir l'instance de dépendance étendue consiste à injecter le fournisseur de services (IServiceProvider
) dans le constructeur du middleware, à créer la méthode scope
dans Invoke
, puis à obtenir le service requis à partir de la portée:
using (var scope = _serviceProvider.CreateScope()) {
var _emailRepository = scope.ServiceProvider.GetRequiredService<IEmailRepository>);
//do your stuff....
}
Consultez la résolution des services dans un corps de méthode dans astuces relatives aux meilleures pratiques d'injection de dépendance fondamentale pour plus de détails.
Le middleware est toujours un singleton, vous ne pouvez donc pas définir de dépendances en tant que dépendances de constructeur dans le constructeur de votre middleware.
Le middleware prend en charge l’injection de méthode sur la méthode Invoke. Vous pouvez donc ajouter e-mail à IEmailRepository en tant que paramètre de cette méthode. Elle y sera injectée et sa portée sera étendue.
public async Task Invoke(HttpContext context, IEmailRepository emailRepository)
{
....
}
Votre middleware
et le service
doivent être compatibles entre eux pour pouvoir injecter le service
via le constructor
de votre middleware
. Ici, votre middleware
a été créé en tant que convention-based middleware
, ce qui signifie qu'il agit en tant que singleton service
et vous avez créé votre service en tant que scoped-service
. Donc, vous ne pouvez pas injecter un scoped-service
dans le constructeur d'un singleton-service
car il oblige le scoped-service
à agir comme un singleton
. Cependant, voici vos options.
InvokeAsync
.middleware
en un factory-based
.Un Factory-based middleware
peut agir comme un scoped-service
. Vous pouvez donc injecter un autre scoped-service
via le constructeur de ce middleware. Ci-dessous, je vous ai montré comment créer un middleware factory-based
.
Ceci est seulement pour la démonstration. Donc, j'ai supprimé tous les autres codes.
public class Startup
{
public Startup()
{
}
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<TestMiddleware>();
services.AddScoped<TestService>();
}
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<TestMiddleware>();
}
}
Le TestMiddleware
:
public class TestMiddleware : IMiddleware
{
public TestMiddleware(TestService testService)
{
}
public Task InvokeAsync(HttpContext context, RequestDelegate next)
{
return next.Invoke(context);
}
}
Le TestService
:
public class TestService
{
}