Search code examples
c#dependency-injectiondryioc

Register dynamic Mapping (or delegate without explicitly resolving)


I have the following registrations prior to the one my question is about:

container.Register<ISettings>();
container.Register<Connection1>();
container.Register<Connection2>();

Connection1 and Connection2 both implement an the interface IConnection. To simplify my case ISettings just contains a property UseConnection1.

What I want is to register a mapping or delegate for service type IConnection that expects ISettings as a dependency and returns either Connection1 or Connection2 as its implementation type.

RegisterMapping itself does not have such capabilities due to the fact, that one can't use any depending services to specify the mapping dynamically.

RegisterDelegate on the other hand has the capabilities to get a specific depending service, but expects the user to return the implementation itself, not its type, like that:

container.RegisterDelegate<ISettings, IResolver, IConnection>((settings, resolver) =>
{
    var serviceType = settings.UseConnection1 ? typeof(Connection1) : typeof(Connection2);
    return resolver.Resolve<IConnection>(serviceType);
});

As seen, that implementation would be got from the IResolver which is far from perfect, as described in the DryIoc wiki.

What I wish to do is something like this:

container.RegisterSomething<IConnection>((ISettings settings) => settings.UseConnection1 ? typeof(Connection1) : typeof(Connection2));

Is this possible in any way with DryIoc?


Solution

  • First, I agree with @steven that the runtime data should be treated with care and better be solved outside of DI. Especially, if it is mutable and being used as a condition.

    Second, if unavoidable, I would've used the factory delegate from the comments as the least surprise solution: RegisterDelegate<ISettting, IConnection>(settings => /*connection construction*/).

    Last, the heads-on answer would be the condition-based resolution and can be done with the condition setup:

    The live code

    using System;
    using DryIoc;
                        
    public class Program
    {
        public static void Main()
        {
            var container = new Container(); 
            container.Register<ISettings, Settings>(Reuse.Singleton);
            container.Register<IConnection, Connection1>(setup: Setup.With(condition: r => r.Container.Resolve<ISettings>().UseConnection1));
            container.Register<IConnection, Connection2>(setup: Setup.With(condition: r => r.Container.Resolve<ISettings>().UseConnection1 == false));
            
            var settings = container.Resolve<ISettings>();
            settings.UseConnection1 = true;
            var conn1 = container.Resolve<IConnection>();
            Console.WriteLine(conn1.GetType());
            
            settings.UseConnection1 = false;
            var conn2 = container.Resolve<IConnection>();
            Console.WriteLine(conn2.GetType());
        }
        
        public interface ISettings
        {
            bool UseConnection1 { get; set; }
        }
        
        public class Settings : ISettings { public bool UseConnection1 { get; set; } }
    
        public interface IConnection {}
        public class Connection1 : IConnection {}
        public class Connection2 : IConnection {}
    }
    

    Update:

    There is another option closer to what you have originally imagined:

    container.Register<IConnection>(made: Made.Of(r =>
        r.Container.Resolve<ISettings>().UseConnection1 ? typeof(Connection1) : typeof(Connection2)));