user2209131
user2209131

Reputation: 317

How to inject constructor which has two times the same interface with Autofac

I want to replace my Unity IoC in my Xamarin 3.2 App with Autofac IoC. Because I haven't any experience with Autofac and the documentation doesn't explain what I need, I hope anyone can help me.

I don't know, how I have to configure the constructor injection, if the class gets two interfaces of the same type in the constructor, but with different implementations. My example shows two repositories and a facade. I want to give both repositories (same interface type) the facade constructor.

What I have

public class HostManager : IHost
    {
           public HostManager()
           {
           }
    }
    public class CustomerRepository : IRepository
    {
           public CustomerRepository(Context context)
           {
           }
    }
    public class AgentRepository : IRepository
    {
           public AgentRepository(Context context)
           {
           }
    }
    public class ToDoFacade : IFacade
    {
    	public ToDoFacade(IHost host, IRepository agentRepository, IRepository customerRepository)
    	{
    	}
    }

    // IoC registration

    public class Registry
    {
        public Registry()
        {
    	var builder = new ContainerBuilder();
    	builder.RegisterType<HostManager>().As<IHost>().SingleInstance();
    	builder.RegisterType<AgentRepository>().As<IRepository>().PreserveExistingDefaults();
    	builder.RegisterType<CustomerRepository>().Named<IRepository>("Customer");

// How can I continue here?
    	builder.RegisterType<ToDoFacade>().As<IFacade>().UsingConstructor(...);
        }
    }

Do you have an idea for me, how I can solve it? Links to resources are also welcome.

Best Tino

Upvotes: 1

Views: 946

Answers (3)

Tobias Moe Thorstensen
Tobias Moe Thorstensen

Reputation: 8981

If you have registered two implementations of the same interface, you can inject an IEnumerable<IYourInterface> to the class. If you need to select the implementation based on some conditions the autofac documentation is pretty neat.

Upvotes: 0

KozhevnikovDmitry
KozhevnikovDmitry

Reputation: 1720

You can use ResolvedParameter to inject certain named registrations to your class. Here is an example with test:

[Test]
public void InjectCertainRegistration()
{
    // Arrange 
    var registry = new Registry();

    // Act
    var facade = registry.GetFacade();

    // Assert
    Assert.IsInstanceOf<AgentRepository>(facade.AgentRepository);
    Assert.IsInstanceOf<CustomerRepository>(facade.CustomerRepository);
}

// IoC registration
public class Registry
{
    private readonly IContainer _root;

    public Registry()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<HostManager>().As<IHost>().SingleInstance();
        builder.RegisterType<AgentRepository>().As<IRepository>().Named<IRepository>("Agent").PreserveExistingDefaults();
        builder.RegisterType<CustomerRepository>().Named<IRepository>("Customer");

        builder.RegisterType<ToDoFacade>().As<IFacade>()
            .WithParameter(new ResolvedParameter((p, ctx) => p.Name == "agentRepository",
                (p, ctx) => ctx.ResolveNamed<IRepository>("Agent")))
            .WithParameter(new ResolvedParameter((p, ctx) => p.Name == "customerRepository",
                (p, ctx) => ctx.ResolveNamed<IRepository>("Customer")));

        _root = builder.Build();
    }

    public IFacade GetFacade()
    {
        return _root.Resolve<IFacade>();
    }
}

public class ToDoFacade : IFacade
{
    /// <summary>
    /// Just for testing
    /// </summary>
    public IRepository AgentRepository { get; }

    /// <summary>
    /// Just for testing
    /// </summary>
    public IRepository CustomerRepository { get; }

    public ToDoFacade(IHost host, IRepository agentRepository, IRepository customerRepository)
    {
        AgentRepository = agentRepository;
        CustomerRepository = customerRepository;
    }
}

public class CustomerRepository : IRepository
{
    public CustomerRepository()
    {
    }
}
public class AgentRepository : IRepository
{
    public AgentRepository()
    {
    }
}

public interface IFacade
{
    /// <summary>
    /// Just for testing
    /// </summary>
    IRepository AgentRepository { get; }

    /// <summary>
    /// Just for testing
    /// </summary>
    IRepository CustomerRepository { get; }
}

public class HostManager : IHost
{
    public HostManager()
    {
    }
}

public interface IHost { }

public interface IRepository { }

I have modified your example a little bit. Method Registry.GetFacade() returns IFacade instance from container. Also I have added repository properties to make sure about their types in tests. Constructor of repositories became parameterless just to simplify the example. Hope it helps.

Upvotes: 0

mdameer
mdameer

Reputation: 1540

If you want to inject a specific implementation with Autofac, you can simply inject the class itself, but you will need to make it injectable by itself by adding AsSelf to the registration definition:

builder.RegisterType<AgentRepository>().AsSelf().As<IRepository>();
builder.RegisterType<CustomerRepository>().AsSelf().As<IRepository>().Named<IRepository>("Customer");

Then, you can inject it directly:

public class ToDoFacade : IFacade
{
    public ToDoFacade(IHost host, AgentRepository agentRepository, CustomerRepository customerRepository)
    {
    }
}

Also, if you want to inject all IRepository implementations, you can simply do:

public ToDoFacade(IHost host, IEnumerable<IRepository> repositories)
{
}

Upvotes: 2

Related Questions