views:

875

answers:

4

Although this question is related to StructureMap, my general question is:

When wiring up components with an IoC container in code (as opposed to configuring via xml) do you generally need explicit project/build references to all assemblies?

Why the separate assemblies? Because:


"Abstract classes residing in a separate assembly from their concrete implementations are a great way to achieve such separation." -Framework Design Guidelines p.91


Example:

Let's say I have PersonBase.dll and Bob.dll

Bob inherits from the abstract class PersonBase. They're both in the Person namespace. But in different assemblies.

I'm programming to PersonBase, not Bob.

Back in my main code, I need a person. StructureMap can scan assemblies. Great, I'll ask StructureMap for one!

Now, in my main code, I am of course referring only to PersonBase, not to Bob. I actually don't want my code to know anything about Bob. No project references, no nuthin. That's the whole point.

So I want to say:

//Reference: PersonBase.dll (only)
using Person;  
...

//this is as much as we'll ever be specific about Bob:
Scan( x=> { x.Assembly("Bob.dll"); }

//Ok, I should now have something that's a PersonBase (Bob). But no ?
ObjectFactory.GetAllInstances<PersonBase>().Count == 0

No luck. What does work is being explicit that I want Bob:

//Reference: PersonBase.dll and Bob.dll
using Person; 
...
Scan( x => {x.Assembly("Bob.dll"); }

//If I'm explicit, it works. But Bob's just a PersonBase, what gives?
ObjectFactory.GetAllInstances<Bob>().Count == 1 //there he is!

But now I've had to reference Bob.dll in my project which is exactly what I didn't want.

I can avoid this situation using Spring + Xml configuration. But then I'm back to Spring + Xml configuration ... !

Am I missing something with using StructureMap, or as a general principle, do (fluent) IoC configurations need explict references to all assemblies?

Possibly related question: http://stackoverflow.com/questions/508399/structuremap-and-scanning-assemblies

+1  A: 

You can do xml configuration with StructureMap as well. You can even mix them if you want.

There are also StructureMap Attributes you could put in your Bob class to tell StructureMap how to load the assembly. DefaultConstructor is one I end up using from time to time.

Chris Brandsma
Thanks for the response Chris :) If I have to go back to Xml wiring, I'll stick with Spring. I think what I have above is structurally/conceptually sound. My problem is that it seems like Ioc-configured-in-code consistently requires dll references I specifically want to avoid.
Jeffrey Knight
No problem. There is nothing wrong with spring.net either. But do be careful that you don't over-architect yourself as well. The more projects you introduce to a solution the longer your build will take. The more configurable you make something, more more test surface you create.
Chris Brandsma
A: 

The automatic scan option only works when you keep the naming, assembly and namespace conventions. You can manually configure structuremap with a fluent interface. Example:

ObjectFactory.Initialize(initialization => 
   initialization.ForRequestedType<PersonBase>()
    .TheDefault.Is.OfConcreteType<Bob>());
Paco
In this case -- which is basically my 2nd scenario that works but involves the dll reference -- I need the dll reference to Bob.dll. Which is what I want to avoid.
Jeffrey Knight
That's impossible.
Paco
[...] .Is.OfConcreteType<Bob>() //<-- you need a dll reference to Bob.dll for that
Jeffrey Knight
I understand what you mean, but it's still impossible to set up an IoC container without dependencies on the types you want to configure.
Paco
got this sorted out-- see answer below
Jeffrey Knight
A: 

What we do on my current project (which uses AutoFac, not StructureMap, but I think it shouldn't make a difference):

We have the interfaces defining external services that the application uses in a core assembly, let's say App.Core (like your PersonBase).

Then we have the implementations of these interfaces in Services.Real (like Bob.dll).

In our case we also have Service.Fake, which are used for facilitating UI testing with dependencies on other enterprise services and databases, etc.

The front-end "client" application itself (in our case, ASP.NET MVC app) references App.Core.

When the app starts, we use Assembly.Load to load the appropriate "Services" implementation DLL, based on a config setting.

Each of these DLLs has an implementation of IServiceRegistry that returns a list of the services that it implements:

public enum LifestyleType { Singleton, Transient, PerRequest}

public class ServiceInfo {
    public Type InterfaceType {get;set;}
    public Type ImplementationType {get;set;}
    // this might or might not be useful for your app, 
    // depending on the types of services, etc.
    public LifestyleType Lifestyle {get;set;} 
}

public interface IServiceRegistry {
    IEnumerable<ServiceInfo> GetServices();
}

... the application finds this ServiceRegistry via reflection and enumerates through these ServiceInfo instances and registers them on the container. For us, this register-all-services lives in the Web application, but it's possible (and preferable in many cases) to have it in a separate assembly.

This way we can isolate the domain logic from the infrastructure code, and prevent "just-this-once" work-arounds where the application ends up depending on a direct reference to the infrastructure code. We also avoid having to have a reference to the container in each Services implementation.

One really important thing if you are doing this: make sure that you have tests that verify that you can create each "top-level" type (in our case, ASP.NET MVC Controllers) with each potential configuration of the IOC container.

Otherwise, it is pretty easy to forget to implement one interface and break huge sections of your application.

+7  A: 
Jeffrey Knight