Question For You Dependency Injection Buffs

code, tdd 0 comments suggest edit

I’m currently doing some app building with ASP.NET MVC in which I try to cover a bunch of different scenarios. One scenario in particular I wanted to cover is approaching an application using a Test Driven Development approach. I especially wanted to cover using various Dependency Injection frameworks, to make sure everything plays nice.

Since I’ve already seen demos with Castle Windsor and Spring.NET, I wanted to give StructureMap a try. Here is the problem I’ve run into.

Say I have a class like so:

public class MyController : IController
{
  MembershipProvider membership;
  public HomeController(MembershipProvider provider)
  {
    this.membership = provider;
  }
}

As you can see, this class has a dependency on the abstract MembershipProvider class, which is passed to this class via a constructor argument. In my unit tests, I can use RhinoMocks to dynamically create a mock that inherits MembershipProvider provider and pass that mock to this controller class. It’s nice for testing.

But eventually, I need to use this class in a real app and I would like a DI framework container to create the controller for me. Here is my StructureMap.config file with some details left out.

<?xml version="1.0" encoding="utf-8" ?>
<StructureMap>
  <PluginFamily Type="IController" DefaultKey="HomeController"
      Assembly="...">
    <Plugin Type="HomeController" ConcreteKey="HomeController"
        Assembly="MvcApplication" />
  </PluginFamily>
</StructureMap>

If I add an empty constructor to HomeController, this code allows me to create an instance of HomeController like so.

HomeController c = 
  ObjectFactory.GetNamedInstance<IController>("HomeController")
  as HomeController;

But when I remove the empty constructor, StructureMap cannot create an instance of HomeController. I would need to tell StructureMap (via StructureMap.config) how to construct an instance of MembershipProvider to pass into the constructor for HomeController

Normally, I would just specify a type to instantiate as another PluginFamily entry. But what I really want to happen in this case is for StructureMap to call a method or delegate and use the value returned as the constructor argument.

In other words, I pretty much want something like this:

<?xml version="1.0" encoding="utf-8" ?>
<StructureMap>
  <PluginFamily Type="IController" DefaultKey="HomeController"
      Assembly="...">
    <Plugin Type="HomeController" ConcreteKey="HomeController"
        Assembly="MvcApplication">
      <Instance>
        <Property Name="provider">
          <![CDATA[
            return Membership.Provider;
          ]]>
        </Property>
      </Instance>
    </Plugin>
  </PluginFamily>
</StructureMap>

The made up syntax I am using here is stating that when StructureMap is creating an instance of HomeController, execute the code in the CDATA section to get the instance to pass in as the constructor argument named provider.

Does anyone know if something like this is possible with any of the Dependency Injection frameworks out there?Whether via code or configuration?

Tags: TDD , Dependency Injection , IoC , StructureMap

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

13 responses

  1. Avatar for Chris Martin
    Chris Martin November 26th, 2007

    If you are just totally against letting IoC do it's thing, you just do it yourself at runtime.
    Not sure about StructureMap...
    I usually have a class for resolving objects from Windsor.
    public IController HomeController{
    get{
    MembershipProvider provider = Membership.Provider;
    return IoC.Resolve<IController>("home-controller", new object[]{provider});
    }
    }

  2. Avatar for Nigel Sampson
    Nigel Sampson November 26th, 2007

    I use the Factory Support Facility from Castle Windsor to achieve exactly what you're trying here. I essentially have a "ProviderFactory" class with a method CreateMembershipProvider that instantiates it from the web.config.
    Sorry I don't have the code with me (it's at work) but a quick search for Factory Support should get you what you're after.
    Hope this helps.

  3. Avatar for Joe
    Joe November 26th, 2007

    Don't know how to do it with StructureMap, but using Windsor, Hammett recently posted article showing how to inject a SiteMapProvider into a MR ViewComponent.
    I think you could follow the same technique for a MembershipProvider.
    http://hammett.castleprojec...

  4. Avatar for Jeremy D. Miller
    Jeremy D. Miller November 26th, 2007

    Phil,
    There isn't anyway in StructureMap 2.0 to do anything like that. I suspect that it'll be in 2.1 shortly though. It'll be in the programmatic Fluent Interface, not the configuration xml though.
    I'd do it by going for the classic 2 constructor path and mark the no arg constructor like this:
    [DefaultConstructor] // forces StructureMap to use this ctor instead of the greedier one below
    public HomeController(){
    // set up the provider thingie
    }
    // Testing constructor
    public HomeController(provider){
    _provider = provider;
    }
    As you might suspect, I never use the Provider stuff.

  5. Avatar for Julian Birch
    Julian Birch November 26th, 2007

    I just spent an entertaining, if fruitless time trying to con the interceptor framework to do this, but it's not really a public API. Being able to register your own IInstanceFactory<T>/Factory<T> methods would definitely be desireable.
    However, in the particular case we're talking about, it should actually be possible to hoodwink the system. Since the provider is actually fixed, we can use the stubbing code to do it.
    StructureMapConfiguration
    .BuildInstancesOf<MembershipProvider>()
    .TheDefaultIsConcreteType<MembershipProvider>();
    ObjectFactory.InjectStub(typeof(MembershipProvider), Membership.Provider);
    You'd put that code in the AppStart somewhere. However, this all relies on the fact that the membership provider never changes. Jeremy's solution is a lot more elegant. ;-)

  6. Avatar for Bill Pierce
    Bill Pierce November 26th, 2007

    I might have missed something but is there a reason you wouldn't just do:

    public HomeController(): this(Membership.Provider) { }
    public HomeController(MembershipProvider provider) { ... }

    If your container has a MembershipProvider configured, it should invoke the proper constructor and inject it. If no MembershipProvider is configured, it should call the no-arg constructor.

  7. Avatar for Jacques Philip
    Jacques Philip November 26th, 2007

    Isn't the Membership provider a particular case as it already has a way of 'injecting' a different provider into an application with the provider pattern.
    So if you configure a MockProvider in the app.config of the test assembly, it will automatically be used by the test controller without having to use DI.
    It seems to me that using DI with a system designed for the provider pattern will get people confused. It is hard enough to understand DI with a system that has been designed for it.

  8. Avatar for Joshua Flanagan
    Joshua Flanagan November 26th, 2007

    Another solution would be to inject a Provider factory, as opposed to the Provider itself.
    public HomeController(IProviderFactory providerFactory){
    this.membership = providerFactory.GetMembershipProvider();
    }
    Then you have an AspNetProviderFactory (which can be passed in by StructureMap) that just returns the statically configured providers. Your tests use a fake provider factory that returns fake providers.

  9. Avatar for Haacked
    Haacked November 27th, 2007

    Thanks everyone. I like Jeremy's and Bill's solution. Simple, elegant, and what I want really. I was having a total brain lapse in not thinking of that.

  10. Avatar for Trumpi
    Trumpi November 27th, 2007

    This is possible with Spring.NET:
    <object id="HomeController"
    type="Foo.HomeController, Assembly.Foo">
    <constructor-arg index="0">
    <object type="Foo.TypeWithDelegateMethod, Assembly.Foo" factory-method="CreateMembershipProvider" />
    </constructor-arg>
    </object>

  11. Avatar for Magnus M&#229;rtensson
    Magnus M&#229;rtensson November 27th, 2007

    Hi!
    Enterprise Library uses attributes to mark injection points - constructor parameters, property setters and even methods (which are executed after all is done - useful for Init() type methods).
    If you use the attribute DependencyAttribute you can specify which type you depend upon which is usually an interface and also you can specify a string key. Then you can specify at app startup which policies should govern your creations. You can set a default policy and also specific ones which govern the creation of your dependent objects.
    For instance you can have this attribute:
    public class MyController : IController
    {
    MembershipProvider membership;
    [InjectionConstructor]
    public HomeController([CreateNew]MembershipProvider provider)
    {
    this.membership = provider;
    }
    }
    Normally I'd prefer to use an interface so I can - via policy - direct which implementor is injected. The [CreateNew] attribute would instead be [Dependency(CreateType = typeof(IMembershipProvider), Name="Home Membership Provider", NotPresentBehavior = NotPresentBehavior.CreateNew)]. Where IMembershipProvider is the interface implemented in MembershipProvider. This way I can easily dynamically proxy it for test, I can proxy it for Policy Injection (AOP) and I can set policies for which type it maps to:
    policyList.Set<ITypeMappingPolicy>(new TypeMappingPolicy(typeof(MembershipProvider), valueID), typeof(IMembershipProvider), "Home Membership Provider")
    The policy is returned when I ask the type mapper strategy for the keyType and keyId.
    Finally ;~) I have also created a config section which adds policies to my system from config.
    Cheers,
    /Magnus

  12. Avatar for Jesse Johnston
    Jesse Johnston November 27th, 2007

    I recently wrote a configuration-free service locator class that does just what you're asking: allow for registration of a factory method in code:
    ServiceLocator locator = new ServiceLocator();
    locator.Register<ICat>(delegate() {
    return kitty.IsSleeping ? buffy : kitty;
    });
    ICat cat = locator.Get<ICat>(); // returns either buffy or kitty.
    Complete details at http://www.teamjohnston.net....
    Cheers,
    Jesse

  13. Avatar for asma
    asma March 24th, 2010

    hi!
    I've a school project using ASP.NET MVC (I've used LinqToSql, repository) and now I've to make add the dependency injection but I couldn't understand a thing about Spring.net :(
    Can you help me with a useful link or post .
    Thanks