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.
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.
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."
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.
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.
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();
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With