DI в ServiceFabrice Service Remoting

У меня есть приложение Service Fabric с одной службой, доступной для Интернета (GatewayService) через веб-API ASP.NET, и несколькими внутренними службами, не доступными для Интернета (назовем одну из них InternalService). Пока InternalService также является веб-API ASP.NET, поэтомуInternalService.cs имеет CreateServiceInstanceListeners() метод, который выглядит так:

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new[] {
        new ServiceInstanceListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
                WebHost.CreateDefaultBuilder()
                    .UseStartup<Startup>()
                    .ConfigureServices((context, services) => { services.AddSingleton(serviceContext); })
                    .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                    .UseUrls(url)
                    .Build()))
    };
}

Класс Startup (в Startup.cs) для InternalService настраивает некоторые службы, такие как добавление SQL DbContext в систему внедрения зависимостей и, конечно же, настройка ASP.NET с помощьюAddMvc() и т.д. У меня есть несколько ApiController, которые предоставляют API.

Это работает, НО я не получаю никакой реальной безопасности типов с этим, и обычно это делает разработку немного громоздкой, так как необходимо вручную десериализовать результат в моем GatewayService, прежде чем манипулировать им. Поэтому я решил использовать вместо этого сервис SF, что привело кCreateServiceInstanceListeners() метод, который выглядит так:

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return this.CreateServiceRemotingInstanceListeners();
}

Затем я скопировал всю логику с контроллеров в InternalService.cs тоже, но это привело к проблеме: у меня больше нет доступа к моему DbContext, потому что он был введен в конструктор ApiController, созданный ASP.NET в соответствии с правилами, установленными в Startup класс, который больше не используется.

  1. Есть ли способ использовать Startup таким же образом при использовании Service Remoting?
  2. Могу ли я разделить API на несколько классов точно так же, как ApiControllers разделяются на несколько классов? Я чувствую, что наличие всех открытых методов в одном классе будет довольно неприятно.

2 ответа

Решение

Вы можете использовать Autofac, есть целая страница, объясняющая, как его настроить:

  • Добавьте пакет NuGet Autofac.ServiceFabric
  • Настроить DI:

      // Start with the trusty old container builder.
      var builder = new ContainerBuilder();
    
      // Register any regular dependencies.
      builder.RegisterModule(new LoggerModule(ServiceEventSource.Current.Message));
    
      // Register the Autofac magic for Service Fabric support.
      builder.RegisterServiceFabricSupport();
    
      // Register a stateless service...
      builder.RegisterStatelessService<DemoStatelessService>("DemoStatelessServiceType");
    
      // ...and/or register a stateful service.
      // builder.RegisterStatefulService<DemoStatefulService>("DemoStatefulServiceType");
    
      using (builder.Build())
      {
        ServiceEventSource.Current.ServiceTypeRegistered(
          Process.GetCurrentProcess().Id,
          typeof(DemoStatelessService).Name);
    
        // Prevents this host process from terminating so services keep running.
        Thread.Sleep(Timeout.Infinite);
      }
    
  • ознакомьтесь с демонстрационным проектом.

Я знаю, что это уже принятый ответ, но я хочу добавить свои два цента.

Как вы уже поняли, удаленное взаимодействие имеет два основных отличия от WebApi:

  1. Учитывая интерфейс удаленного взаимодействия, у вас есть единственный класс реализации

  2. Класс реализации удаленного взаимодействия является одноэлементным, поэтому, даже если вы используете DI, как объяснено в принятом ответе, вы все равно не можете внедрить DbContext для каждого запроса.

Я могу предложить вам несколько решений этих проблем:

  1. Это просто: создайте больше интерфейсов. Вы можете добавить столько интерфейсов удаленного взаимодействия, сколько захотите, в одну службу Service Fabric. Итак, вы должны разделить ваш удаленный API на более мелкие интерфейсы с группами, которые имеют смысл (разделение интерфейсов). Но я не думаю, что у вас должно быть много, потому что это, вероятно, означает, что у вашего микросервиса слишком много обязанностей.

  2. Наивный подход к наличию зависимостей для каждого запроса заключается во внедрении фабрик в класс удаленного взаимодействия, чтобы вы могли разрешать и удалять зависимости в каждом методе вместо внедрения конструктора. Но я нашел гораздо лучший подход с помощью Mediatr, который может показаться нетривиальным, но после настройки его очень легко использовать. Это работает так: вы создаете небольшой вспомогательный класс, который получаетILifetimeScope(поскольку вы используете Autofac) в конструкторе, и он предоставляет метод Execute. Этот метод создаст дочерний LifetimeScope, разрешит Mediatr и отправитWrapperRequest<TRequest>(оболочка - это уловка, позволяющая исключить зависимость объектов ввода и вывода удаленного взаимодействия от Mediatr). Это позволит вам реализовать класс Handler для каждой операции удаленного взаимодействия, которая будет разрешаться для каждого запроса, чтобы вы могли внедрить зависимости в конструктор, как вы это делаете с контроллером WebApi.

Это может показаться запутанным, если вы не знакомы с Mediatr и Autofac. Если будет время, напишу об этом в блоге.