我在理解代码中的错误源时遇到了问题。我尝试着讲有关.net核心中的微服务的课程。运行构建解决方案后,我得到:
------- Project finished: CrossX.Services.Identity. Succeeded: True. Errors: 0. Warnings: 0
但是当我运行它时,我得到:
/opt/dotnet/dotnet /RiderProjects/crossx/src/CrossX.Services.Identity/bin/Debug/netcoreapp2.2/CrossX.Services.Identity.dll
Unhandled Exception: System.InvalidOperationException: Cannot resolve scoped service 'CrossX.NETCore.Commands.ICommandHandler`1[CrossX.NETCore.Commands.CreateUser]' from root provider.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(Type serviceType, IServiceScope scope, IServiceScope rootScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at CrossX.NETCore.Services.ServiceHost.BusBuilder.SubscribeToCommand[TCommand]() in /RiderProjects/crossx/src/CrossX.NETCore/Services/ServiceHost.cs:line 78
at CrossX.Services.Identity.Program.Main(String[] args) in /RiderProjects/crossx/src/CrossX.Services.Identity/Program.cs:line 11
当我添加到webHostBuilder .UseDefaultServiceProvider(options => options.ValidateScopes = false)时,我的问题已解决。但是据我所知,关闭验证并不是一个好主意。另外,当我将AddScope更改为AddTransient时,问题已解决(或至少已运行)。
问题是我不知道在哪里可以找到此错误的来源。我想我不太了解哪里出了问题,因此,如果有人可以帮助我,或者至少提供一个提示,我将不胜感激。
这是我的
Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddRabbitMq(Configuration);
services.AddScoped<ICommandHandler<CreateUser>, CreateUserHandler>();
services.AddScoped<IEncrypter, Encrypter>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
Program.cs
public class Program
{
public static void Main(string[] args)
{
ServiceHost.Create<Startup>(args)
.UseRabbitMq()
.SubscribeToCommand<CreateUser>()
.Build()
.Run();
}
}
ServiceHost.cs
public class ServiceHost : IServiceHost
{
private readonly IWebHost _webHost;
public ServiceHost(IWebHost webHost)
{
_webHost = webHost;
}
public void Run() => _webHost.Run();
public static HostBuilder Create<TStartup>(string[] args) where TStartup : class
{
Console.Title = typeof(TStartup).Namespace;
var config = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddCommandLine(args)
.Build();
var webHostBuilder = WebHost.CreateDefaultBuilder(args)
.UseConfiguration(config)
// .UseDefaultServiceProvider(options => options.ValidateScopes = false)
.UseStartup<TStartup>();
return new HostBuilder(webHostBuilder.Build());
}
public abstract class BuilderBase
{
public abstract ServiceHost Build();
}
public class HostBuilder : BuilderBase
{
private readonly IWebHost _webHost;
private IBusClient _bus;
public HostBuilder(IWebHost webHost)
{
_webHost = webHost;
}
public BusBuilder UseRabbitMq()
{
_bus = (IBusClient) _webHost.Services.GetService(typeof(IBusClient));
return new BusBuilder(_webHost, _bus);
}
public override ServiceHost Build()
{
return new ServiceHost(_webHost);
}
}
public class BusBuilder : BuilderBase
{
private readonly IWebHost _webHost;
private IBusClient _bus;
public BusBuilder(IWebHost webHost, IBusClient bus)
{
_webHost = webHost;
_bus = bus;
}
public BusBuilder SubscribeToCommand<TCommand>() where TCommand : ICommand
{
var handler = (ICommandHandler<TCommand>) _webHost.Services.GetService(typeof(ICommandHandler<TCommand>));
_bus.WithCommandHandlerAsync(handler);
return this;
}
public BusBuilder SubscribeToEvent<TEvent>() where TEvent : IEvent
{
var handler = (IEventHandler<TEvent>) _webHost.Services.GetService(typeof(IEventHandler<TEvent>));
_bus.WithEventHandlerAsync(handler);
return this;
}
public override ServiceHost Build()
{
return new ServiceHost(_webHost);
}
}
}
答案 0 :(得分:1)
无法从根提供商处解析作用域服务
ICommandHandler<CreateUser>
如错误所述,您不能从根提供者创建作用域服务实例。根提供程序是存在于服务范围之外的根服务提供程序。因此,它无法解析仅应在服务范围内使用的服务。
如果要从根提供者解析作用域服务,例如当您从单例服务中使用它时,应首先使用IServiceScopeFactory
创建服务作用域:
var serviceScopeFactory = _webHost.Services.GetService<IServiceScopeFactory>();
using (var scope = serviceScopeFactory.CreateScope())
{
var handler = (IEventHandler<TEvent>)scope.ServiceProvider.GetService(typeof(IEventHandler<TEvent>))
// …
}
请注意,服务范围应该是短暂的,您需要在事后将其处置以进行清理。
看您的实现,似乎您将范围服务传递给其他服务以订阅事件。通常这似乎是一个坏主意,因为这意味着在整个应用程序生命周期中,(可能)单例服务将保留对范围服务的引用。这通常是个坏主意(就像我说的那样,作用域服务应该只存在很短的时间)。
您应该问自己,为什么需要在此处对服务进行范围划分,以及是否无法使它们成为单例。或者,如果您实际上需要基于实例的订阅机制(而不是例如仅使用工厂模式或类型的类型)。