Search code examples
dryioc

How do you resolve multiple dependencies using serviceKey to match parameter.name?


I am trying to figure out how to get DryIoc to resolve ITest on ExampleClass?

This is matching the parameter name to the service key as there are multiple registrations to locate the correct service.

   public class Program
    {
        public void Main()
        {
            var container = new Container();
            
            container.Register<ITest, A>(serviceKey: "a");
            container.Register<ITest, B>(serviceKey: "b");
            container.Register<ExampleClass>();

            var example = container.Resolve<ExampleClass>();
        }
    }
    
    public interface ITest { }
    public class A : ITest { }
    public class B : ITest { }
    public class ExampleClass
    {
        public ExampleClass(ITest a, ITest b)
        {
        }
    }

Solution

  • Use Parameters.Of https://www.fuget.org/packages/DryIoc.dll/4.2.5/lib/netstandard2.0/DryIoc.dll/DryIoc/Parameters

    public class Program 
    { 
        public void Main() 
        { 
            var c = new Container();
            c.Register<ITest, A>(serviceKey: "a");
            c.Register<ITest, B>(serviceKey: "b");
            c.Register<ExampleClass>(made:
                Made.Of(parameters: Parameters.Of
                    .Name("a", serviceKey: "a")
                    .Name("b", serviceKey: "b")));
            var example = c.Resolve<ExampleClass>(); 
        }
    }
    

    You may also omit the Made.Of(parameters: because ParameterSelector returned by Parameters.Of is implicitly convertable to Made:

            c.Register<ExampleClass>(made:
                Parameters.Of
                    .Name("a", serviceKey: "a")
                    .Name("b", serviceKey: "b"));
    

    You may apply more generic matching of parameter name to service key without explicitly listing the parameters, but it will be more fragile given you will add non-keyed parameter later:

            c.Register<ExampleClass>(made:
                Parameters.Of.Details(
                    (req, parInfo) => ServiceDetails.Of(serviceKey: parInfo.Name)));
    

    Another type-safe option is directly specifying the constructor via delegate expression (Linq.Expressions.Expression<T>) describing its positional arguments - this option will inform you with compilation error when constructor is changed:

            c.Register<ExampleClass>(made:
                Made.Of(() =>
                    new ExampleClass(
                        Arg.Of<ITest>(serviceKey: "a"),
                        Arg.Of<ITest>(serviceKey: "b"))));
    

    The above ways applied on the specific registration, but the same may be done on Container level using Rules:

    var c = new Container(rules =>
        rules.With(parameters:
            Parameters.Of.Details(
                (req, parInfo) => req.ServiceType == typeof(ExampleClass) 
                    ? ServiceDetails.Of(serviceKey: parInfo.Name) 
                    : null)
    ));
    

    Note: The last option affects performance because the rule need to be checked for all registrations.

    The same approaches can be applied for specifying the property injection using PropertiesAndFields.Of.