views:

4097

answers:

4

(from the StockTraderRIBootstrapper.cs file in the Prism V2 StockTrader example app)

What is the difference between this:

ShellPresenter presenter = new ShellPresenter();

and this:

ShellPresenter presenter = Container.Resolve<ShellPresenter>();
  • I understand the second example is treating the container like a factory, walking up to it saying "I need an instantiated object of type ShellPresenter".
  • But what if, e.g. I need to send parameters, what would be the equivalent of "new ShellPresenter(1, true)" etc.?
  • And since the Container has to be told about the ShellPresenter, I expected to find somewhere in the project a place where the ShellPresenter class being registered with the container, e.g. I was expecting

something like this:

Container.RegisterType<IShellPresenter, ShellPresenter>();

but found it nowhere. So how does the container get to know about these types so it can .Resolve them? I rebuilt this in its own project and get a "Resolution of the dependency failed" error, where do I need to register this dependency then?

Any direction/discussion here would be helpful.

Unexplained Answer:

So, in the bootstrapper, when I register the Shell itself:

protected override void ConfigureContainer()
{
    Container.RegisterType<IShellView, Shell>();
    base.ConfigureContainer();
}

then the Container can resolve the ShellPresenter type. So how is the ShellPresenter type registered when I register the Shell type?

The Surprising Answer:

Ok, so it turns out that you don't have to register the type you are trying to resolve but you do have to register the parameter (interface) types passed to the constructor of the type you are trying to resolve, i.e. since I inject the IShellView interface into my ShellPresenter's constructor, I needed to register the IShellView type and not the IShellPresenter type:

public ShellPresenter(IShellView view) ...

I tested this by trying to resolve the type Tester:

Tester tester = Container.Resolve<Tester>();

As long as I inject SomeClass into its constructor:

public Tester(ISomeClass someClass)

I get unresolved dependency errors until I register SomeClass with the container:

Container.RegisterType<ISomeClass, SomeClass>();

Then it works. This is as surprising as it is educational. Needs to sink in. I'm going to go get a coffee and think about this for awhile.

If anyone can elaborate on why this is the case, it would be much appreciated.

+1  A: 

Well, I can't answer for Untiy, but for Castle Windsor, the registration could be in the app.config/web.config file. There is also the ability to add the parameters in the config xml.

This allows you to change the implementation and configuration of the object without having to recompile you application.

James Curran
interesting, ok, I couldn't find it in App.config though, I'm working through the StockTrader demo in the Prism V2 guidelines.
Edward Tanguay
+1  A: 

You understand the basics.

There are overloads for resolving types that require constructor arguments. Alternatively, you can always code your types to have a parameterless constructor.

The point of DI containers is that you can configure them to change the type that gets resolved for a particular interface without recompiling your software. The code example you provided for configuring the provider can't be changed at runtime. That's why most dependency injectors allow you to configure them in app.config/web.config/some other external configuration file. That way you can reconfigure your app to inject a different type without recompiling, which is the true power of DI frameworks like Unity.

Will
but where is the ShellPresenter being registered with the container? The only contact I can find between the two is that the ShellPresenter gets the container injected in its constructor. ShellPresenter is not in the app.config.
Edward Tanguay
Look, its not magic. In your example app, either its being registered in code or its being registered in a configuration file. If you haven't found it its because you're looking in the wrong place.
Will
Also, provide a link to the demo project; I can't find it anywhere.
Will
As soon as I register IShellView as a type with the container, the container is able to resolve ShellPresenter. This is because ShellPresenter gets IShellView injected in its constructor. See above. The odd thing is ShellPresenter is never registered yet I can resolve it.
Edward Tanguay
Edward Tanguay
+1  A: 

In Unity, there is indeed a Container.RegisterType<TFrom, TTo>() method set that registers types at runtime. It's probably more common to do it using an XML configuration file, but either works.

Interestingly, in Unity there is no Container.Resolve<T>(params object[] parameters) -type method to resolve a type with specific constructor parameter values. Unity is built on top of ObjectBuilder which is the P&P team's library for doing object construction and wireup (IIRC it was originally written for ObjectSpaces but has been significantly enhanced now). ObjectBuilder gives you the ability to inject dependencies in various ways, including via the constructor, so you could say - for example - that you would pass a new instance of a type it's dependent on into the constructor of a resolved type; but that type would also have to be registered. You can also pass instances of registered types (a registered instance / singleton etc). But AFAICS there is no way to simply give it a value to pass in.

I think doing that would go against the philosophy of IoC to some extent, which is why they don't provide that facility. The container should, in theory, be able to give you a complete object graph in any given circumstance, so you should never have to pass parameters in, and making your objects dependent on constructor parameters other than injectable object dependencies (which the container will resolve for you) is seen as Bad Design.

I can't speak for Windsor, StructureMap or the others, which may allow you to do this. I can't even say categorically that Unity has no way to do it since I'm reasonably new at it, but IIRC Chris Tavares - who basically built Unity - hangs out here from time to time, so maybe he'll drop by and answer this one :-)

DotNetGuy
+3  A: 

If you try to resolve a concrete class and have not registered an instance or sub-class to satisfy it, then Unity will construct an instance of the concrete class for you, resolving any dependencies that it has.

So when you ask for ShellPresenter, and haven't registered it, Unity just new's up a ShellPresenter for you with the ShellView as a parameter.

Ray Henry
+1 - after reading this simple statement a few times, you've made quite a bit of sense!
Metro Smurf