Search code examples
c#dependency-injectionignite

Dependency injection in Apache Ignite.NET service


Consider Apache Ignite.NET cluster that provides service grid.

There is a simple service, that will run on any node:

public class ClientConnectionService : IClientConnectionService, IService
{
    private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();

    [InstanceResource] private IIgnite Ignite { get; set; }

    public void Listen(string hostname, int port, uint username, string password,
        ClientConnectionListenerOptions options = ClientConnectionListenerOptions.All)
    {
        Logger.Debug("Listen");
    }

    public void Init(IServiceContext context)
    {
        Logger.Debug("Initialized");
    }

    public void Execute(IServiceContext context)
    {
        Logger.Debug("Executed");
    }

    public void Cancel(IServiceContext context)
    {
        Logger.Debug("Canceled");
    }
}

The application is using Castle Windsor as inversion of control container.

I would like to inject custom dependencies, that won't be serialized and transferred over the wire.

Is there any way to achieve it?

N.B. In Java version, there is @SpringResourceannotation that will basically do what I want, but the question is about .NET, that provides just [InstanceResource] attribute.


Solution

  • This is what I have ended up with:

    1. In shared project where all the interfaces and contracts are described I've introduced IContainer
        public interface IContainer
        {
            T Resolve<T>();
        }
    
    1. In project that is responsible for Apache Ignite.NET integration I've implemented simple Apache Ignite.NET plugin
        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)
            {
    
            }
        }
    
    1. In main project, that is responsible for wiring up all components, I've implemented IContainer, defined previously, and registered it in Castle Windsor:
        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>()
                );
            }
        }
    
    1. In the very same project I've registered 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()}
                        }))
                );
            }
        }
    
    1. Finally, in application's main method:
        // 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();
        }
    

    That's it. From now on, I am able to access IContainer implementation in Apache Ignite.NET distributed service like this:

        var plugin = Ignite.GetPlugin<DependencyInjectionPlugin>("DependencyInjection");
        var whatever = plugin.Resolve<IWhatever>();