Разрабатывая различные приложения, использующие популярную библиотеку Castle Windsor для внедрения зависимостей и Apache Ignite.NET в качестве «ключика», который открывает дверь в «облачные» вычисления, я столкнулся с небольшим неудобством: у меня не было никакой возможности внедрить зависимость в сервис, запускаемый через так называемый Service Grid.
Причина по которой это происходит довольна банальна. Apache Ignite.NET сериализует сервис, отправляет его на один из доступных серверов, где он десериализуется и запускается. Так как этот процесс никаким образом не имеет понятия о Castle Windsor, мы получаем то, что получаем.
Для решения этой проблемы нам необходимо создать плагин для Apache Ignite.NET, который получит контейнер, отвечающий за внедрение зависимостей и предоставить возможность сервису обратиться к нему, для получения того или иного объекта.
Первым делом внедрим дополнительный уровень абстракции для контейнера, обеспечивающего внедрения зависимостей, для того чтобы в будущем мы могли легко сменить его имплементацию на другую:
public interface IContainer
{
T Resolve<T>();
}
public class DependencyInjectionContainer : IContainer
{
protected IKernel Kernel { get; set; }
public DependencyInjectionContainer(IKernel kernel)
{
Kernel = kernel;
}
public T Resolve<T>()
{
return Kernel.Resolve<T>();
}
}
public class DependencyInjectionInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component
.For<IContainer>()
.ImplementedBy<DependencyInjectionContainer>()
);
}
}
Для создания плагина нам необходимо создать 3 класса: класс, отвечающий за конфигурацию плагина, провайдер плагина и, непосредственно, сам плагин.
public class DependencyInjectionPlugin
{
public IContainer Container { get; set; }
public T Resolve<T>()
{
return Container.Resolve<T>();
}
}
[PluginProviderType(typeof(DependencyInjectionPluginProvider))]
public class DependencyInjectionPluginConfiguration : IPluginConfiguration
{
public void WriteBinary(IBinaryRawWriter writer)
{
// No-op
}
public int? PluginConfigurationClosureFactoryId { get; } = null; // No Java part
}
public class DependencyInjectionPluginProvider : IPluginProvider<DependencyInjectionPluginConfiguration>
{
public string Name { get; } = "DependencyInjection";
public string Copyright { get; } = "MIT";
protected DependencyInjectionPlugin DependencyInjectionPlugin { get; set; }
public T GetPlugin<T>() where T : class
{
return DependencyInjectionPlugin as T;
}
public void Start(IPluginContext<DependencyInjectionPluginConfiguration> context)
{
DependencyInjectionPlugin = new DependencyInjectionPlugin();
}
public void Stop(bool cancel)
{
}
public void OnIgniteStart()
{
}
public void OnIgniteStop(bool cancel)
{
}
}
Отлично, осталось загрузить плагин в Apache Ignite.NET.
public class IgniteInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component
.For<IIgnite>()
.UsingFactoryMethod(() => Ignition.Start(new IgniteConfiguration
{
PluginConfigurations = new[] {new DependencyInjectionPluginConfiguration()}
}))
);
}
}
При старте приложения мы, как обычно, инициализируем контейнер внедрения зависимостей, однако теперь мы готовы передать его плагину, который только что написали:
var Done = new ManualResetEventSlim(false);
// Build Windsor container
using (var container = new WindsorContainer())
{
// Install DI abstraction layer
container.Install(new DependencyInjectionInstaller());
// Install cluster abstraction layer
container.Install(new IgniteInstaller());
// Attach DI container to cluster plugin
container
.Resolve<IIgnite>()
.GetPlugin<DependencyInjectionPlugin>("DependencyInjection")
.Container = container.Resolve<IContainer>();
// Wait
Done.Wait();
}
Поздравляю, если вы дочитали до конца, то вы сможете получить любую зависимость из вашего контейнера внутри сервиса. Выглядит это так:
public class ClientConnectionService : IClientConnectionService, IService
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
[InstanceResource] private IIgnite Ignite { get; set; }
public void Init(IServiceContext context)
{
Logger.Debug("Initialized");
}
public void Execute(IServiceContext context)
{
var plugin = Ignite.GetPlugin<DependencyInjectionPlugin>("DependencyInjection");
var whatever = plugin.Resolve<IWhatever>();
whatever.DoSomething();
}
public void Cancel(IServiceContext context)
{
Logger.Debug("Canceled");
}
}
Автор: unnutz