.NET Core 2.0 中间件构造函数依赖注入(Middleware inject)报错问题解决方法

本文主要介绍在.NET Core 2.0中,使用中间件构造函数注入实例报错:InvalidOperationException: Cannot resolve 'API.Domain.Data.Repositories.IEmailRepository' from root provider because it requires scoped service 'A,解决此错误异常的方法及示例代码。

出现错误异常的示例代码

1) 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();
}
}

2) 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;
}
}
}

3) 处理异常中间件(Middleware)代码

public class ExceptionHandlingMiddleware
{
    private const string ErrorEmailAddress = "errors@ourdomain.com";
    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>();
    }
}

解决错误异常的3种方法

1)修改中间件代码解决

IEmailRepositoryStartup类别中将其注册为范围服务。这意味着您不能将其作为构造函数参数注入,因为中的构造函数注入Middleware只能Singleton解析服务Middleware。应该将依赖项移动到这样的Invoke方法:

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);
}
}

2) 通过IServiceProvider获取依赖实例

using (var scope = _serviceProvider.CreateScope()) {
var _emailRepository = scope.ServiceProvider.GetRequiredService<IEmailRepository>);
//.....
}

3) 关闭作用域嵌套验证

.NET Core 1.1中默认为关闭,.NET Core 2.0中默认打开的,关闭后就不会在运行时检查范围级别是否嵌套不正确。修改Program.cs文件的BuildWebHost方法,如下:

public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseDefaultServiceProvider(options =>
options.ValidateScopes = false)
.Build();
}

参考文档https://github.com/aspnet/DependencyInjection/issues/578

推荐阅读
cjavapy编程之路首页