15

Hi I have been having trouble trying to tell Unity that for an Interface if it has multiple implementations , I want it to inject them in different classes.Here is what I mean:

Let's say I have an interface IProductCatalogService and two implementations ProductCatalog : IProductCatalogService and ProductCatalogService : IProductCatalogService.

How would I go about telling Unity that for Class A I want in my constructor passed an instance of type ProductCatalog and for Class B I want an instance of ProductCatalogService.

I am using Unity in an ASP.NET Web API project and I have set the resolver in the GLobalConfiguration.

For simple 1 to 1 registrations everything works.

Here is what I have tried but it does not seem to work:

public class DependencyServiceModel
{
    public Type From { get; set; }
    public Type To { get; set; }
    public IEnumerable<Type> ForClasses { get; set; }
}

public void RegisterTypeForSpecificClasses(DependencyServiceModel dependencyService)
{
    foreach (var forClass in dependencyService.ForClasses)
    {
        string uniquename = Guid.NewGuid().ToString();

        Container.RegisterType(dependencyService.From, 
            dependencyService.To, uniquename);

        Container.RegisterType(forClass, uniquename, 
            new InjectionConstructor(
                new ResolvedParameter(dependencyService.To)));
    }
}

In the DependencyServiceModel, From is the interface, To is the object to witch I want to instantiate and ForClasses are the Type for which I want to use the To Object.

Steven
  • 166,672
  • 24
  • 332
  • 435
aleczandru
  • 5,319
  • 15
  • 62
  • 112

2 Answers2

30

In the example below you have an interface implemented twice and injected on demand into two different client classes, just as you request. The trick is to use named registrations.

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();
        container.RegisterType<IFoo, Foo1>("Foo1");
        container.RegisterType<IFoo, Foo2>("Foo2");

        container.RegisterType<Client1>(
            new InjectionConstructor(new ResolvedParameter<IFoo>("Foo1")));
        container.RegisterType<Client2>(
            new InjectionConstructor(new ResolvedParameter<IFoo>("Foo2")));

        Client1 client1 = container.Resolve<Client1>();
        Client2 client2 = container.Resolve<Client2>();
    }
}

public interface IFoo {  }
public class Foo1 : IFoo {  }
public class Foo2 : IFoo { }

public class Client1
{
    public Client1(IFoo foo) { }
}

public class Client2
{
    public Client2(IFoo foo) { }
}

This is most probably what you do wrong:

Container.RegisterType(forClass, uniquename, 
    new InjectionConstructor(
        new ResolvedParameter(dependencyService.To)));

You create a named registration for your concrete class. Instead you should have

Container.RegisterType(forClass, null, 
    new InjectionConstructor(
        new ResolvedParameter(dependencyService.To, uniquename)));
Steven
  • 166,672
  • 24
  • 332
  • 435
Wiktor Zychla
  • 47,367
  • 6
  • 74
  • 106
  • Is there any way of doing this without generics I can not provide an exact type at the point of registration as you can see from my code – aleczandru Sep 06 '13 at 20:30
  • Of course, unity has registration methods in both generic and nongeneric versions. – Wiktor Zychla Sep 06 '13 at 20:38
  • I still don't understand how this helps. In your example you use types Client1 and Client2 to distinguish the distinct map paths. But what if you only had Client1 and it needed to use Foo1 in some instances and Foo2 in other instances. How at runtime does the container know which one to use? If you are forced to specify manually using some business rules or logic, what is the point of the IoC container? – Ristogod Aug 15 '14 at 21:27
  • @Ristogod: business logic around resolving smells. You'd rather have a composite service that uses two internally but also has some business knowledge. Technically you could have an injection factory and implement the logic there but it sounds like improper mixing of responsibilities (domain layer should be responsible for business logic rather than infrastructure layer (ioc)). – Wiktor Zychla Aug 15 '14 at 22:04
1

Nice to know. If you register multiple types to an Interface like belove;

container.RegisterType<ITransactionsService, EarningsManager>();
container.RegisterType<ITransactionsService, SpendingsManager>();

you can not get the list of types by;

IEnumerable<ITransactionsService> _transactionsService;

here in the list will be always the last registered type ( SpendingsManager.)

To prevent this situation ;

container.RegisterType<ITransactionsService, EarningsManager>("EarningsManager");
container.RegisterType<ITransactionsService, SpendingsManager>("SpendingsManager");

You have to change the code in this way.