简体   繁体   中英

How to resolve a collection of interfaces with Unity?

I have a class with a constructor which takes a collection of child interfaces

public class Manager : IManager
{
   public Manager(IEnumerable<IDataAccess> dataAccess)
   {

IDataAccess is a base interface

public interface IFooDataAccess : IDataAccess
{

public interface IBarDataAccess : IDataAccess
{

public interface IBazDataAccess : IDataAccess
{

There are concrete implementations for each of these. When I resolve IManager I want the dataAccess parameter to be populated with a collection of FooDataAccess, BarDataAccess and BazDataAccess.

I'm using unity as an IoC, the current registration is:

    public static void RegisterTypes(IUnityContainer container)
    {
        container.RegisterTypes(AllClasses.FromAssemblies(
                typeof(IManager).Assembly),
            WithMappings.FromMatchingInterface,
            WithName.Default,
            WithLifetime.ContainerControlled);

I do have a few other classes in the solution too (hence using a registration pattern) so I'd like to keep this. I've been playing around with trying to register IManager on it's own:

        var allDataAccesses = container.ResolveAll<IDataAccess>();
        var parameter = new InjectionConstructor(allDataAccesses);
        container.RegisterType<IManager, Manager>(parameter);

But I can't get it to work, what am I missing. How can I configure unity to resolve the constructor?

The registrations that you did will map the directly implemented interfaces and not IDataAccess .

For example, it will map IFooDataAccess to FooDataAccess . What you need is to map IDataAccess to FooDataAccess for example.

To solve this, add the following registration code:

container.RegisterTypes(
    typeof (IManager)
        .Assembly
        .GetTypes()
        .Where(x => x.IsClass)
        .Where(x => typeof (IDataAccess).IsAssignableFrom(x)),
    x => new[] {typeof (IDataAccess)},
    WithName.TypeName, WithLifetime.ContainerControlled);

This will search the assembly for classes that implement IDataAccess (directly or indirectly), and will create a map between IDataAccess and these classes.

One important thing to note here is that it gives the registration a name. This is important because if you have the interface IDataAccess mapped to many classes, then you cannot use the default name. How would unity know which map entry to use?

Another thing you need to do is to map IEnumerable<IDataAccess> to IDataAccess[] like this:

container.RegisterType<IEnumerable<IDataAccess>, IDataAccess[]>();

Unity knows how to resolve arrays by looking at named registrations for the required interface. It cannot by default do the same for IEnumerable<T> . An alternative for this is to change the constructor to use an array like this:

public class Manager : IManager
{
    public Manager(IDataAccess[] dataAccess)
    {
        ....
    }

    ....
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM