tags:

views:

960

answers:

4

I'm using Prism, which gives be the nice Unity IoC container too. I'm new to the concept, so I haven't gotten my hands all around it yet. What I want to do now is to create an object using the IoC container, but passing an extra parameter too. Allow me to explain with an example..:

I have a class that takes a commands object. This is registered in the IoC container, so it will handle it nicely:

public class Person 
{
    public Person(IApplicationCommands commands) { .. }
    ..
}

Person person = _container.Resolve<Person>();

Now - I want to pass in another argument - e.g. the name of the person. However, I still want to use the IoC container to handle the resolving and hence get the other paramenters from the IoC container. But pass in the name as a "custom" parameter. Can this be done?

public class Person 
{
    public Person(IApplicationCommands commands, string name) { .. }
    ..
}

string name = "John"; 
Person person = _container.Resolve<Person>(name); // ....??

This example doesn't seem to work, but is there a way to make it work? Or does Unity IoC container require all parameters to be registered in the container before calling Resolve?

A: 

I can't think of any way to do this with constructor injection. I think you'll need to use property injection for the dependencies (marked with the Dependency attribute) and then either take just the string in the constructor, instantiate yourself, then use BuildUp to wire the dependencies, or make the string a property too that you set manually after Resolve.

Steven Robbins
+2  A: 

You've got a few options. They are honestly a bit lame, but they will work.

Option 1: Scoped Container

If you want to use constructor injection, you'll need to create a scoped container and put your data into that scoped container:

IUnityContainer subContainer = _container.CreateChildContainer();

//Don't do this... create a custom type other than string, like
// MyConstructorParams or something like that... this gets the point across.
subContainer.RegisterInstance<string>("John");
Person person = subContainer.Resolve<Person>();

Option 2: Initialize Method

What I typically do, though, is have a seperate Initialize method on my target objects for instance variables:

public class Person
{
     public Person(IApplicationCommands commands)
     { .. }
     public void Initialize(string name) { .. }

     ..
}

And then your usage becomes:

Person person = container.Resolve<Person>();
person.Initialize("John");

Neither is particularly pleasant, but it'll get the job done. The important thing is to pick a convention and stick to it, otherwise you'll get a bit lost.

Hope this helps.

Anderson Imes
I wouldn't recommend either of these approaches. While employing scoped containers or registering logically named instances are certainly valid techniques for some scenarios, they aren't appropriate given the presumed context of creating new DDD entities. You certainly couldn't predict what new Person objects might need to be created if this is tied to new customer registration, and even if you could ... well, I think you understand the problem that would present.The second option is certainly more viable, but its best to express required dependencies through constructor parameters ...
Derek Greer
This also doesn't address what I see to be a flaw in the modeling of Person to begin with, and the direct use of the container as a service locator for the creation of such a type.
Derek Greer
Certainly the scoped container option is the sillier of the two options, but it does work. I also assumed that the OP dumbed down his question and he isn't trying to do anything like this on a simple Entity, but a more complex service object. I have this problem all of the time with these objects where the object has some dependencies, but also needs some instance data passed to it that I need to act on when it is passed. This is why other IoC containers, like Ninject, support exactly what the OP wants. Unity just doesn't yet.
Anderson Imes
Thanks for your feedback guys. Yes @Imes - I certainly dumbed down the question..
stiank81
My apologies. For some reason I assumed your child container registration of the string "John" was happening at app start up which is why I deemed it impractical. You are correct that this is a viable solution, but I'd still advise against using any container as a service locator in this context.
Derek Greer
+2  A: 

There are a few choices you might consider:

In the case where you need to create a new entity which has legitimate dependencies in addition to any data being supplied (e.g. customer name), encapsulate this into a factory which itself has been injected into the calling object:

Person person = _personFactory.CreatePerson("bubba");

The factory can be injected with the entity's dependencies and supplied to the constructor if required or set by other means if optional:

var person = new Person("bubba", _domainService);


For transient-variable dependencies, such as a strategy used by a particular method, use Double Dispatch:

public class Person
{
    public void DoSomethingWith(SomeStrategy strategy)
    {
        strategy.DoSomething(this);
    }
 }


Derek Greer
This is only true if you are assuming Person is just a simple entity, which is valid in this case, but doesn't address this shortcoming in Unity for all cases. Ninject and a few other IoC containers have the ability to do exactly what the OP wants, but Unity does not, leaving us with some pretty limited options. I agree the scoped container is about the nastiest thing ever (I don't do this personally), but it is a solution.
Anderson Imes
Person is just a fictionous class to simplify this question. In my scenario I don't do this for a domain class, but a ViewModel representation of the domain class. I'm using Prism, and the "Person" view in this case will have to trigger commands to show some sub-data in certain regions, and for that I need the IApplicationCommands.
stiank81
@Anderson Imes: I wasn't aware some containers enabled this. I can imagine some cases where this might be useful, but as a whole I still recommend a factory or double dispatch approach over using a container as a service locator, as these approaches preserve infrastructure ignorance. At the very least, you could limit your code's dependency upon direct invocations to the container by encapsulating the container specific technique in the factory.
Derek Greer
I agree 100% with all of your points. I provided the scoped container solution only because it allows Unity to work like other containers like Ninject. The second solution was meant to be an alternative to using the container at all (this is what I do in my projects where I'm using Unity rather than Ninject).
Anderson Imes
+5  A: 

http://stackoverflow.com/questions/787001/can-i-pass-constructor-parameters-to-unitys-resolve-method

container.Resolve<IFoo>(new ParameterOverrides<Foo> { { "name", "bar" }, { "address", 42 } });"
NotDan
Hey! Very nice! I hadn't seen this but it's similar to what Ninject has added recently. I'll have to pull down the latest version, looks like. Thanks!
Anderson Imes
What happens if you change the parameter name in the constructor?
HeavyWave
I'm guessing no workie..
NotDan