Search code examples
c#dryioc

DryIoc - inject a Func with additional parameters as a Factory-method


In my implementation, I need to dynamically resolve a service, based on the servicekey. Therefor, I want to pass a Func, but can't get that working. Any help on this is appreciated.

Let's have the following implemention:

public interface IMyInterface { 
  void Foo();
}
public class ClassA : IMyInterface { 
  void Foo() => BarA();
}
public class ClassB : IMyInterface { 
  void Foo() => BarB();
}
public class ClassC : IOtherInterface { 
  private readonly Func<string, IMyInterface> getInstance_;
  void ClassC(Func<string, IMyInterface> getInstance)
  {
    getInstance_ = getInstance;
  }

  void SomeLogic() 
  {
    IMyInterface implementation = getInstance("keyA");
    implementation.Foo();
  }
}
public static void Main()
{
  var c = new Container();
  c.Register<IMyInterface, ClassA>(serviceKey:"keyA");
  c.Register<IMyInterface, ClassB>(serviceKey:"keyB");
  c.Register<IOtherInterface, ClassC>();

  c.Resolve<IOtherInterface>(); 
}

The last call fails, which I understand. The exception thrown is:

DryIoc.ContainerException: 'Unable to resolve IMyInterface with passed arguments [_String0] IsWrappedInFunc, IsDirectlyWrappedInFunc
  in wrapper Func<String, IMyInterface> FactoryId=10 with passed arguments [_String0] IsResolutionCall
  from Container with Scope {no name}
 with Rules with {TrackingDisposableTransients} and without {ThrowOnRegisteringDisposableTransient}
  with normal and dynamic registrations:
  ("keyA", {FactoryID=32, ImplType=ClassA})  ("keyB", {FactoryID=33, ImplType=ClassB}) '

I figured out how to register this with a Delegate:

c.RegisterDelegate<Func<string, IMyInterface>>(r => (key) => c.Resolve<IMyInterface>(key));

But in the documentation, DryIoc recommends using factory-methods above Delegates. I've played a lot with Made.Of-notations (which I find poorly documented), but have not found a working one.

Is this possible with a (static) Factory-method?


Solution

  • If you need to dynamically select the service based on the key but without relying on Service Locator anti-pattern (without injecting the IResolver into ClassC) - you may inject all available service factories as described here https://github.com/dadhi/DryIoc/blob/master/docs/DryIoc.Docs/RegisterResolve.md#keyed-registrations in the second example:

    public class ClassC : IOtherInterface { 
      private readonly Func<string, IMyInterface> _implementations;
      void ClassC(KeyValuePair<string, Func<IMyInterface>>[] implementations)
      {
        _implementations = implementations;
      }
    
      void SomeLogic() 
      {
        var implementationFactory = _implementations.First(x => x.Key == "keyA").Value;
        var implementation = implementationFactory();
        implementation.Foo();
      }
    }
    

    BTW, The same approach is taken in Autofac and maybe by other DI containers capable to compose the wrappers like Func, Lazy and the collections.

    BTW2, If you don't like the KeyValuePair you may use Tuple instead.