Не удается разрешить службу с областью действия - PullRequest
0 голосов
/ 10 марта 2019

У меня проблема с пониманием источника ошибок в моем коде. Я пытаюсь получить ход про микросервисы в ядре .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);
            }
        }
    }

1 Ответ

3 голосов
/ 10 марта 2019

Невозможно разрешить службу с областью действия ICommandHandler<CreateUser> от корневого поставщика

Как говорится в сообщении об ошибке, вы не можете создать экземпляр службы с областью действия от корневого поставщика.Корневой поставщик - это корневой поставщик услуг, который существует вне областей обслуживания.Таким образом, он не может разрешить службы, которые должны использоваться только в пределах областей обслуживания.

Если вы хотите разрешить службу с областью действия от корневого поставщика, например, когда вы используете ее изСлужба singleton, вы должны сначала создать область обслуживания, используя IServiceScopeFactory:

var serviceScopeFactory = _webHost.Services.GetService<IServiceScopeFactory>();
using (var scope = serviceScopeFactory.CreateScope())
{
    var handler = (IEventHandler<TEvent>)scope.ServiceProvider.GetService(typeof(IEventHandler<TEvent>))
    // …
}

Обратите внимание, что области действия службы должны быть недолговечными, и что вам нужно будет впоследствии их утилизироватьчтобы очистить.


Глядя на вашу реализацию, кажется, что вы передаете свои сервисы с определенными областями другому сервису для подписки на события.Как правило, это кажется плохой идеей, поскольку это означает, что ссылка на определенную службу будет сохраняться (вероятной) одноэлементной службой в течение всего срока службы приложения.Как правило, это плохая идея (как я уже говорил, сервисы с областями действия должны жить только в течение короткого времени).

Вы должны спросить себя, зачем вам нужны сервисы, которые там находятся, и не можете ли вы сделать их одноразовымивместо.Или, если вам действительно нужно, чтобы механизм подписки основывался на экземпляре (а не, например, только на типе, используя фабричный шаблон или что-то в этом роде).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...