views:

99

answers:

2

Hello. I'm using Structure map and want to inject instance (constructed by container) into controller's property. Instance should be named and stored in http session context container. In the previous version of my application I've used custom DI framework and it was easy enough to make such things:

public class MyController : Controller
{
    [InjectSession("MySessionInstanceKey")]
    public MyManager Manager {get; set;}
}

Is there any easy ways to do it with structuremap ?
Or maybe i can introduce my custom attributes and injection logic into SM framework (extend framework somehow) ?
Please help me to find a way to resolve this and Thanks a lot !

P.S. i've found temporary solution, but it increases cohesion of controller with IoC framework and contains a lot of code:

private const string ordersBulkManagerKey = "_OrdersBulkManager";
public BulkManager OrdersBulkManager
{
    get
    {
        var manager = Session[ordersBulkManagerKey] as BulkManager;
        if(manager == null)
        Session[ordersBulkManagerKey] = manager
            = ObjectFactory.GetInstance<BulkManager>();
        return manager;
    }
}

So, I don't want to use ObjectFactory.GetInstance there ...

+1  A: 

There's a few blog post out there on how to wire up configure ASP.NET MVC and StructureMap together. (So most of the following code is a summary from various posts out there.)

The common way to get this going is by declaring our own controller factory class, which will allow us inject our dependencies via the constructor. (The default controller factory that ASP.NET MVC uses requires a default constructor to be present)

So in your MyController class, you would have a constructor which accept a MyManager parameter (which will be injected for you by our very own controller factory class)

public class MyController : Controller
{
   private readonly ISomeService _someService;
   //Constructor Injection. 
   public MyController(ISomeService someService){
       _someService = someService;
    }
}

Next you would configure ASP.NET MVC do use this new controller factory class (which we will call StructureMapControllerFactory).

protected void Application_Start()
{

   //This is where you register your concrete types with StructureMap
    Bootstrapper.ConfigureStructureMap();

    //Our very own Controller Factory
    ControllerBuilder.Current.SetControllerFactory
    (new StructureMapControllerFactory());

    RegisterRoutes(RouteTable.Routes);
}

In the StructureMapControllerFactory, we call ObjectFactory.GetInstance to you return us the controller (provided we have wired up all our dependencies)

public class StructureMapControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(Type controllerType)
    {
        if (controllerType == null) return null;
        try
        {
            return ObjectFactory.GetInstance(controllerType) as Controller;
        }
        catch (StructureMapException)
        {
            System.Diagnostics.Debug.WriteLine(ObjectFactory.WhatDoIHave());
            throw;
        }
    }
}

Hopefully my explanation is clear, but let me know if it isn't and I can expand on it.

BTW, the snippet below is an example on how your bootstrap code might look like.

public static class Bootstrapper 
{
    public static void ConfigureStructureMap()
    {
        ObjectFactory.Initialize(
         x => x.AddRegistry(new MyApplicationRegistry()));            
    }
}

public class MyApplicationRegistry : Registry
{
    public MyApplicationRegistry()
    {
         ForRequestedType<ISomeService>()
         .CacheBy(InstanceScope.Your_Choice_Here)
         .TheDefault.Is.OfConcreteType<SomeService>();
    }
}

Note: Please refer to StructureMap doc for various InstanceScope options.

Noel
Thanks man ! Constructor injection + lifecycle management = this is what I need
ILICH
+1  A: 

StructureMap can control your object lifecycles so you don't have to implement it yourself

For<IBulkManager>()
    .LifecycleIs(Lifecycles.GetLifecycle(InstanceScope.HttpSession))
    .Use<BulkManager>();

So your code would turn into

public IBulkManager OrdersBulkManager
{
    get { return ObjectFactory.GetInstance<IBulkManager>(); }
}
Jon Erickson
Thanks Jon, it seems that lifecycle and scope parts of logic are gone from my controller, it's good. A reference to ObjectFactory is still there but i don't care really, it was about 8 months ago .. hahaWell, maybe it is not a good style to tie DI container with the rest of a code ... but my example with attribute injection is the same bad style. So: I probably should use lifecycle definitions with constructor injection technique
ILICH
with property setters sometimes you may find that since your object doesn't require them all the time, it may make more sense to have BulkManager be a service that your controller acts as a client of (so that you don't have this property in your controller at all).
Jon Erickson