views:

4729

answers:

6

I am using Microsoft's Unity for dependency injection and I want to do something like this:

  IDataContext context = _unityContainer.Resolve<IDataContext>();
  var repositoryA = _unityContainer.Resolve<IRepositoryA>(context); //Same instance of context
  var repositoryB = _unityContainer.Resolve<IRepositoryB>(context); //Same instance of context

  IDataContext context2 = _unityContainer.Resolve<IDataContext>(); //New instance
  var repositoryA2 = _unityContainer.Resolve<IRepositoryA>(context2);

RepositoryA and RepositoryB both have a constructor that takes an IDataContext parameter, and I want Unity to initialize the repository with the context that I pass it. Also note that IDataContext is not registered with Unity (I dont want 3 instances of IDataContext).

+5  A: 

< 2 cents>

What if you later on decide to use a different service that requires more or less than just the context?

The problem with constructor parameters and IoC is that the parameters are ultimately tied to the concrete type being used, as opposed to being part of the contract that the service interface defines.

My suggestion would be that you either resolve the context as well, and I believe Unity should have a way for you to avoid constructing 3 instances of it, or you should consider a factory service that has a way for you to construct the object.

For instance, what if you later on decide to construct a repository that doesn't rely on a traditional database at all, but instead use an XML file to produce dummy-data for the test? How would you go about feeding the XML content to that constructor?

IoC is based around decoupling code, by tying in the type and semantics of the arguments to the concrete types, you really haven't done the decoupling correctly, there's still a dependency.

"This code can talk to any type of repository possibly, as long as it implements this interface.... Oh, and uses a data context".

Now, I know that other IoC containers have support for this, and I had it in my first version of my own as well, but in my opinion, it doesn't belong with the resolution step.

< /2 cents>

Lasse V. Karlsen
I see your point and agree with you, however I still need the RepositoryA and RepositoryB's instances to have the same IDataContext, which needs to be different than RepositoryC. Also note that IRepositoryA and IRepositoryB has a property for IDataContext. I'll update the sample code a bit.
NotDan
Great point. I was about to add a string parameter to the constructor, but after viewing this point, i decided to make it a full blown object. It only consists of the string at this point, but i can already see how I could add more useful properties to it
Santosh Benjamin
A: 

NotDan, I think you may have answered your own question in comments to lassevk.

First, I would use a LifetimeManager to manage the lifecycle and number of instances of IDataContext that Unity creates.
http://msdn.microsoft.com/en-us/library/cc440953.aspx

It sounds like the ContainerControlledLifetimeManager object will give you the instance management that you need. With that LifetimeManager in place, Unity should add the same instance of the IDataContext to all objects that require an IDataContext dependency.

Jeff Fritz
+2  A: 

The very short answer is: no. Unity currently has no way to pass parameters into the constructor that aren't constant or injected, that I have been able to find. IMHO that's the single biggest thing it's missing, but I think it is by design rather than by omission.

As Jeff Fritz notes, you could in theory create a custom lifetime manager that knows which context instance to inject into various types, but that's a level of hard-coding which seems to obviate the purpose of using Unity or DI in the first place.

You could take a small step back from full DI and make your repository implementations responsible for establishing their own data contexts. The context instance can still be resolved from the container but the logic for deciding which one to use would have to go into the implementation of the repository. It's not as pure, certainly, but it would get rid of the problem.

DotNetGuy
A: 

Another alternative you could use (don't really know if it is a good practice or not) is creating two containers and registering an instance for each:

IDataContext context = _unityContainer.Resolve<IDataContext>();
_unityContainer.RegisterInstance(context);
var repositoryA = _unityContainer.Resolve<IRepositoryA>(); //Same instance of context
var repositoryB = _unityContainer.Resolve<IRepositoryB>(); //Same instance of context


//declare _unityContainer2
IDataContext context2 = _unityContainer2.Resolve<IDataContext>(); //New instance
_unityContainer2.RegisterInstance(context2);
var repositoryA2 = _unityContainer2.Resolve<IRepositoryA>(context2); //will retrieve the other instance

hope this helps too

Samuel Carrijo
+3  A: 

As of today they have added this functionality:

It’s in the latest drop here:

http://unity.codeplex.com/SourceControl/changeset/view/33899

Discussion on it here:

http://unity.codeplex.com/Thread/View.aspx?ThreadId=66434

Example:

container.Resolve<IFoo>(new ParameterOverrides<Foo> { { "name", "bar" }, { "address", 42 } });"
Exist
+1  A: 

You can use InjectionConstructor / InjectionProperty / InjectionMethod depending on your Injection Architecture within the ResolvedParameter< T >("name") to get a instance of a pre-registered Object in the container.

In your case this Object must be registered with a Name, and for the same insance you need ContainerControlledLifeTimeManager() as the LifeTimeManager.

_unityContainer.RegisterType<IDataContext,DataContextA>("DataContextA", new ContainerControlledLifeTimeManager());
_unityContainer.RegisterType<IDataContext,DataContextB>("DataContextB");

  var repositoryA = _unityContainer.Resolve<IRepositoryA>(new InjectionConstructor(
new ResolvedParameter<IDataContext>("DataContextA")));

  var repositoryB = _unityContainer.Resolve<IRepositoryB>(new InjectionConstructor(
new ResolvedParameter<IDataContext>("DataContextA")));

  var repositoryA2 = _unityContainer.Resolve<IRepositoryA>(new InjectionConstructor(
new ResolvedParameter<IDataContext>("DataContextB")));
Trecenti