Внедрение зависимостей в сервис Apache Ignite.NET

Разрабатывая различные приложения, использующие популярную библиотеку 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");
    }
}
Поделиться публикацией

Комментарии 1

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое