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
класс, который больше не используется.
- Есть ли способ использовать
Startup
таким же образом при использовании Service Remoting? - Могу ли я разделить 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:
Учитывая интерфейс удаленного взаимодействия, у вас есть единственный класс реализации
Класс реализации удаленного взаимодействия является одноэлементным, поэтому, даже если вы используете DI, как объяснено в принятом ответе, вы все равно не можете внедрить DbContext для каждого запроса.
Я могу предложить вам несколько решений этих проблем:
Это просто: создайте больше интерфейсов. Вы можете добавить столько интерфейсов удаленного взаимодействия, сколько захотите, в одну службу Service Fabric. Итак, вы должны разделить ваш удаленный API на более мелкие интерфейсы с группами, которые имеют смысл (разделение интерфейсов). Но я не думаю, что у вас должно быть много, потому что это, вероятно, означает, что у вашего микросервиса слишком много обязанностей.
Наивный подход к наличию зависимостей для каждого запроса заключается во внедрении фабрик в класс удаленного взаимодействия, чтобы вы могли разрешать и удалять зависимости в каждом методе вместо внедрения конструктора. Но я нашел гораздо лучший подход с помощью Mediatr, который может показаться нетривиальным, но после настройки его очень легко использовать. Это работает так: вы создаете небольшой вспомогательный класс, который получает
ILifetimeScope
(поскольку вы используете Autofac) в конструкторе, и он предоставляет метод Execute. Этот метод создаст дочерний LifetimeScope, разрешит Mediatr и отправитWrapperRequest<TRequest>
(оболочка - это уловка, позволяющая исключить зависимость объектов ввода и вывода удаленного взаимодействия от Mediatr). Это позволит вам реализовать класс Handler для каждой операции удаленного взаимодействия, которая будет разрешаться для каждого запроса, чтобы вы могли внедрить зависимости в конструктор, как вы это делаете с контроллером WebApi.
Это может показаться запутанным, если вы не знакомы с Mediatr и Autofac. Если будет время, напишу об этом в блоге.