Search code examples
c#ioc-containerdryioc

how to resolve an instance of a specific type (known at runtime) that implements an interface using DryIoc.Container


the title is, maybe, misleading but basically I want the DryIoc.Container to resolve a specific implementation of an interface whose type (the type of the class the implements the interface) is given at runtime ( multiple implementations of the same interface are registered ). I can't use serviceKey to identify implementations because the code that resolves the implementation is expected to do something like : container.Resolve<IService>(*** here specify the runtime type of the implementation ***) to get the implementation we want (the type of the implementation is acquired via a configuration we read at runtime).

    using System;
    using DryIoc;
    namespace DryIoc_example
    {
        interface IService { }
        class ServiceImpl_1 : IService { }
        class ServiceImpl_2 : IService { }

        class Program
        {
            static void Main(string[] args)
            {
                var container = new Container();
                // register my implementations of IService
                // this could be done at runtime using
                // container.Register(typeof(IService), typeof(ServiceImpl_1));
                container.Register<IService, ServiceImpl_1>();
                container.Register<IService, ServiceImpl_2>();

                // now, i want the container to resolve a specific
                // implementation of IService ( ServiceImpl_2 in this case)

                // this line doesn't work and throws
                var myService = container.Resolve<IService>(typeof(ServiceImpl_2));

                // this line is expected to print the type of ServiceImpl_2
                Console.WriteLine(myService.GetType());
            }
        }
    }

`

the code above throws :

Unable to resolve DryIoc_example.IService {RequiredServiceType=DryIoc_example.ServiceImpl_2}

Where CurrentScope: null

  and ResolutionScope: null

  and Found registrations:

  DefaultKey.Of(0),{ID=20, ImplType=DryIoc_example.ServiceImpl_1}}

  DefaultKey.Of(1),{ID=21, ImplType=DryIoc_example.ServiceImpl_2}}

I know I can get all the registered implementations for an interface and filtering the one that has the implementation I want (using code similar to this response https://stackoverflow.com/a/37069854/5767019 by the maintainer of DryIoc ), but I couldn't figure out a way to make the container resolve it when I ask it to !


Solution

  • The answer for DryIoc, Spring.Net's GetObjectsOfType equivalent? is pretty much on the spot.

    Repeating the options here:

    Using implementation type as service key

    container.Register<IService, ServiceImpl_1>(serviceKey: typeof(ServiceImpl_1));
    container.Resolve<IService>(serviceKey: typeof(ServiceImpl_1));
    

    Using RegisterMany

    This will do registration with all implemented public types as service types, including implementation type itself:

    using System;
    using DryIoc;
    namespace DryIoc_example
    {
        interface IService {}
        class ServiceImpl_1 : IService {}
        class ServiceImpl_2 : IService {}
    
        public class Program
        {
            public static void Main()
            {
                var container = new Container();
                
                container.RegisterMany<ServiceImpl_1>(nonPublicServiceTypes: true);
                container.RegisterMany<ServiceImpl_2>(nonPublicServiceTypes: true);
                
                var myService = container.Resolve<IService>(typeof(ServiceImpl_2));
                
                // this line is expected to print the type of ServiceImpl_2
                Console.WriteLine(myService.GetType());
            }
        }
    }
    

    Live example

    Select from container registrations manually

    Find the registered factory with given implementation type, and get the actual default key used for registration. Resolve using the key:

    using System;
    using System.Linq;
    using DryIoc;
    
    namespace DryIoc_example
    {
        public interface IService {}
        class ServiceImpl_1 : IService {}
        class ServiceImpl_2 : IService {}
    
        public class Program
        {
            public static void Main()
            {
                    var container = new Container();
    
                    container.Register<IService, ServiceImpl_1>();
                    container.Register<IService, ServiceImpl_2>();
                
                    var myService = container.TryResolveByImplementation<IService>(typeof(ServiceImpl_1));
                    
    
                    // this line is expected to print the type of ServiceImpl_1
                    Console.WriteLine(myService.GetType());
            }
        }
        
        public static class ContainerExtensions
        {
            public static TService TryResolveByImplementation<TService>(this IContainer container, Type implementationType)
            {
                var factory = container.GetAllServiceFactories(typeof(TService))
                    .FirstOrDefault(f => f.Value.ImplementationType == implementationType);
                
                return factory != null 
                    ? container.Resolve<TService>(serviceKey: factory.Key) 
                    : default(TService);
            }
        }
    }
    

    Live example