C# 无法从根提供程序.Net核心2解析作用域服务
当我尝试运行我的应用程序时,我得到了错误C# 无法从根提供程序.Net核心2解析作用域服务,c#,asp.net-core,asp.net-core-2.0,C#,Asp.net Core,Asp.net Core 2.0,当我尝试运行我的应用程序时,我得到了错误 InvalidOperationException: Cannot resolve 'API.Domain.Data.Repositories.IEmailRepository' from root provider because it requires scoped service 'API.Domain.Data.EmailRouterContext'. 奇怪的是,据我所知,这个EmailRepository和接口的设置与我的所有其他存储库完全相
InvalidOperationException: Cannot resolve 'API.Domain.Data.Repositories.IEmailRepository' from root provider because it requires scoped service 'API.Domain.Data.EmailRouterContext'.
奇怪的是,据我所知,这个EmailRepository和接口的设置与我的所有其他存储库完全相同,但没有为它们抛出任何错误。仅当我尝试使用应用程序时才会发生错误。UseMailingExceptionHandling();线路。这是我的一些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();
}
}
最后是异常处理中间件
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>();
}
}
公共类异常处理中间件
{
私有常量字符串ErrorEmailAddress=”errors@ourdomain.com";
专用只读IEmailRepository\u emailRepository;
private readonly RequestDelegate\u next;
公共例外处理中间件(RequestDelegate next,IEmailRepository emailRepository)
{
_下一个=下一个;
_emailRepository=emailRepository;
}
公共异步任务调用(HttpContext上下文)
{
尝试
{
wait_next.Invoke(上下文);
}
捕获(例外情况除外)
{
Wait HandleExceptionAsync(上下文,例如,_emailRepository);
}
}
私有静态任务HandleExceptionAsync(HttpContext上下文,异常,
IEmailRepository(电子邮件存储库)
{
var code=HttpStatusCode.InternalServerError;//500如果意外
var email=新电子邮件
{
Body=exception.Message,
FromAddress=ErrorEmailAddress,
Subject=“API错误”,
ToAddress=ErrorEmailAddress
};
emailRepository.SendEmail(电子邮件);
context.Response.ContentType=“应用程序/json”;
context.Response.StatusCode=(int)代码;
返回context.Response.WriteAsync(“发生错误”);
}
}
公共静态类AppErrorHandlingExtensions
{
公共静态IApplicationBuilder使用EmailingExceptionHandling(此IApplicationBuilder应用程序)
{
如果(app==null)
抛出新的ArgumentNullException(nameof(app));
返回app.useMediddleware();
}
}
更新:
我找到了这个链接,它使我从这个链接更改了Program.cs文件的BuildWebHost方法
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
公共静态IWebHost BuildWebHost(字符串[]args)
{
返回WebHost.CreateDefaultBuilder(args)
.UseStartup()
.Build();
}
对此
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseDefaultServiceProvider(options =>
options.ValidateScopes = false)
.Build();
}
公共静态IWebHost BuildWebHost(字符串[]args)
{
返回WebHost.CreateDefaultBuilder(args)
.UseStartup()
.UseDefaultServiceProvider(选项=>
options.ValidateScopes=false)
.Build();
}
我不知道到底发生了什么,但它现在似乎起作用了。中间件始终是单体的,因此在中间件的构造函数中不能将作用域依赖项作为构造函数依赖项 中间件支持Invoke方法上的方法注入,因此您只需将IEmailRepository emailRepository作为参数添加到该方法中,它将被注入其中,并且在范围上也可以
public async Task Invoke(HttpContext context, IEmailRepository emailRepository)
{
....
}
您将
IEmailRepository
注册为Startup
类中的作用域服务。
这意味着您不能将其作为构造函数参数注入中间件
,因为只有单例
服务可以通过中间件
中的构造函数注入来解析。您应该将依赖项移动到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);
}
}
获取作用域依赖项实例的另一种方法是将服务提供者(
IServiceProvider
)注入中间件构造函数,在Invoke
方法中创建scope
,然后从作用域中获取所需的服务:
using (var scope = _serviceProvider.CreateScope()) {
var _emailRepository = scope.ServiceProvider.GetRequiredService<IEmailRepository>();
//do your stuff....
}
使用(var scope=\u serviceProvider.CreateScope()){
var_emailRepository=scope.ServiceProvider.GetRequiredService();
//做你的事。。。。
}
查看方法体中的解析服务了解更多详细信息。您的
中间件
和服务
必须相互兼容,以便通过中间件
的构造函数注入服务。在这里,您的中间件
已被创建为基于约定的中间件
,这意味着它充当单一服务
,并且您已将您的服务创建为范围服务
。因此,不能将作用域服务
注入单例服务
的构造函数,因为它强制作用域服务
充当单例服务。然而,这里有你的选择
将服务作为参数注入InvokeAsync
方法
如果可能的话,让您的服务成为单身服务
将中间件
转换为基于工厂的中间件
基于工厂的中间件
能够充当范围服务
。因此,您可以通过该中间件的构造函数注入另一个作用域服务。下面,我向您展示了如何创建基于工厂的中间件
这只是为了演示。因此,我删除了所有其他代码
public class Startup
{
public Startup()
{
}
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<TestMiddleware>();
services.AddScoped<TestService>();
}
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<TestMiddleware>();
}
}
TestService
:
public class TestService
{
}
这里发生的事情是,范围嵌套没有得到验证;如中所示,它不会在运行时检查范围级别的嵌套是否不正确。显然,这在1.1中是默认关闭的。一旦2.0出现,他们就默认打开了它。对于任何试图关闭ValidateScopes的人,请阅读本文。我遇到了类似的情况,然后我使用AddTransient添加了服务,它能够解决依赖关系。我以为它不会工作,因为中间件是单例的?有点奇怪。我认为一个暂时的依赖关系必须手动处理,而scoped则不同,scoped将在web请求的最后自动处理,在那里它是第一次被创建的。可能在外部对象被释放后,作用域依赖项内的瞬时一次性对象将被释放。但我还是不确定是否有过渡
public class TestMiddleware : IMiddleware
{
public TestMiddleware(TestService testService)
{
}
public Task InvokeAsync(HttpContext context, RequestDelegate next)
{
return next.Invoke(context);
}
}
public class TestService
{
}