Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolving named dependencies with Unity

I have a service with 2 dependencies: a Repository and a Gateway (sms).

I need to resolve 2 different versions of the service that only differ on one of the parameters passed to the gateway.

The code is simplified to as follows

public interface IService
{
    string DoSomething();
}

public interface IServiceFoo
{
    string DoSomething();
}

public interface IServiceBar
{
    string DoSomething();
}

public interface IRepository { }
public class Repository : IRepository { }

public interface IGateway
{
    string Name { get; set; }
}

public class Gateway : IGateway
{
    public string Name { get; set; }
    public Gateway(string name)
    {
        this.Name = name;
    }
}

public class Service : IService, IServiceFoo, IServiceBar
{
    private readonly IGateway _gateway;
    private readonly IRepository _repo;
    public Service(IRepository repo, IGateway gateway)
    {
        _gateway = gateway;
        _repo = repo;
    }

    public string DoSomething()
    {
        return _gateway.Name;
    }
}

Failing test

[TestClass]
public class UnityTest
{
    [TestMethod]
    public void TestMethod1()
    {
        var container = new UnityContainer();
        container
            .RegisterType<IRepository, Repository>()
            .RegisterType<IGateway, Gateway>("FooGateway", new InjectionConstructor("I am foo"))
            .RegisterType<IGateway, Gateway>("BarGateway", new InjectionConstructor("I am bar"))
            .RegisterType<IServiceFoo, Service>(new InjectionConstructor(new ResolvedParameter<IRepository>(), new ResolvedParameter<IGateway>("FooGateway")))
            .RegisterType<IServiceBar, Service>(new InjectionConstructor(new ResolvedParameter<IRepository>(), new ResolvedParameter<IGateway>("BarGateway")));

        var barGateway = container.Resolve<IGateway>("BarGateway");
        var fooGateway = container.Resolve<IGateway>("FooGateway");

        var serviceBar = container.Resolve<IServiceBar>();
        var serviceBarGatewayName = serviceBar.DoSomething();

        var serviceFoo = container.Resolve<IServiceFoo>();
        var serviceFooGatewayName = serviceFoo.DoSomething();

        Assert.AreEqual("I am bar", barGateway.Name); // pass
        Assert.AreEqual("I am foo", fooGateway.Name); // pass


        Assert.AreEqual("I am bar", serviceBarGatewayName); // pass
        Assert.AreEqual("I am foo", serviceFooGatewayName); // FAIL

The wrong gateway is being passed in when the service is resolved, however if I resolve the gateway explicitly by name, it comes out correct. I think I'm missing something fundamental in how ResolvedParameter(string name) is working but I assumed that it looks for a type in the container with that name.

like image 525
drch Avatar asked Apr 23 '13 16:04

drch


People also ask

How to resolve dependencies in Unity?

There could be more methods to resolve dependencies like property injection or a service locator. We used constructor injection in our application. One can make use of other containers instead of Unity.

How to resolve a named dependency in a container?

To tell the container to resolve a named dependency, you'll need to use an InjectionParameter object. For your ClientModel example, do this: This tells the container "When resolving ClientModel, call the constructor that takes a single IRepository parameter. When resolving that parameter, resolve with the name 'Client' in addition to the type."

How to resolve named dependencies at runtime without hardcode?

Furthermore, this technique allows to resolve named dependencies at runtime without having to hardcode the named dependencies in the constructor, in the application configuration file or use InjectionParameter which are all methods that require to know what named dependency to use at design time.

What is a direct dependency in a project?

This is a declaration that you need a specific version of a particular package in order for the project to work. Dependencies that appear in your project manifest are called “direct” dependencies. Packages can also require other packages in order to work. These are called “indirect”, or transitive, dependencies.


2 Answers

Still have no idea why your version doesn't work but this DOES work (as I've expected):

        var container = new UnityContainer();
        container
            .RegisterType<IRepository, Repository>()
            .RegisterType<IGateway, Gateway>( "FooGateway", new InjectionConstructor( "I am foo" ) )
            .RegisterType<IGateway, Gateway>( "BarGateway", new InjectionConstructor( "I am bar" ) )
            //.RegisterType<IServiceFoo, Service>( new InjectionConstructor( new ResolvedParameter<IRepository>(), new ResolvedParameter<IGateway>( "FooGateway" ) ) )
            //.RegisterType<IServiceBar, Service>( new InjectionConstructor( new ResolvedParameter<IRepository>(), new ResolvedParameter<IGateway>( "BarGateway" ) ) );
            .RegisterType<IServiceFoo>( new InjectionFactory( c => new Service( c.Resolve<IRepository>(), c.Resolve<IGateway>( "FooGateway" ) ) ) )
            .RegisterType<IServiceBar>( new InjectionFactory( c => new Service( c.Resolve<IRepository>(), c.Resolve<IGateway>( "BarGateway" ) ) ) );

Note that I am using InjectionFactory instead of InjectionConstructor.

Yet another version that works. This time I keep your way of registering services but I make them named and resolve by name:

        var container = new UnityContainer();
        container
            .RegisterType<IRepository, Repository>()
            .RegisterType<IGateway, Gateway>( "FooGateway", new InjectionConstructor( "I am foo" ) )
            .RegisterType<IGateway, Gateway>( "BarGateway", new InjectionConstructor( "I am bar" ) )
            .RegisterType<IServiceFoo, Service>( "sf", new InjectionConstructor( new ResolvedParameter<IRepository>(), new ResolvedParameter<IGateway>( "FooGateway" ) ) )
            .RegisterType<IServiceBar, Service>( "sb", new InjectionConstructor( new ResolvedParameter<IRepository>(), new ResolvedParameter<IGateway>( "BarGateway" ) ) );
            //.RegisterType<IServiceFoo>( new InjectionFactory( c => new Service( c.Resolve<IRepository>(), c.Resolve<IGateway>( "FooGateway" ) ) ) )
            //.RegisterType<IServiceBar>( new InjectionFactory( c => new Service( c.Resolve<IRepository>(), c.Resolve<IGateway>( "BarGateway" ) ) ) );


        var barGateway = container.Resolve<IGateway>( "BarGateway" );
        var fooGateway = container.Resolve<IGateway>( "FooGateway" );

        var serviceBar = container.Resolve<IServiceBar>( "sb" );
        var serviceBarGatewayName = serviceBar.DoSomething();

        var serviceFoo = container.Resolve<IServiceFoo>( "sf" );
        var serviceFooGatewayName = serviceFoo.DoSomething();
like image 178
Wiktor Zychla Avatar answered Sep 30 '22 14:09

Wiktor Zychla


it can also be done using ParameterOverride as below

        container
            .RegisterType<IRepository, Repository>()
            .RegisterType<IGateway, Gateway>("FooGateway", new InjectionConstructor("I am foo"))
            .RegisterType<IGateway, Gateway>("BarGateway", new InjectionConstructor("I am bar"))
            .RegisterType<IServiceFoo, Service>(new InjectionConstructor(new ResolvedParameter<IRepository>(), typeof(IGateway)))
            .RegisterType<IServiceBar, Service>(new InjectionConstructor(new ResolvedParameter<IRepository>(), typeof(IGateway)));

        var barGateway = container.Resolve<IGateway>("BarGateway");
        var fooGateway = container.Resolve<IGateway>("FooGateway");

        var serviceBar = container.Resolve<IServiceBar>(new ParameterOverride("gateway", barGateway));
        var serviceBarGatewayName = serviceBar.DoSomething();

        var serviceFoo = container.Resolve<IServiceBar>(new ParameterOverride("gateway", fooGateway));
        var serviceFooGatewayName = serviceFoo.DoSomething();

        Assert.AreEqual("I am bar", barGateway.Name); // pass
        Assert.AreEqual("I am foo", fooGateway.Name); // pass


        Assert.AreEqual("I am bar", serviceBarGatewayName); // pass
        Assert.AreEqual("I am foo", serviceFooGatewayName); // pass
like image 33
Hemendr Avatar answered Sep 30 '22 13:09

Hemendr