2

I have an asp.net-mvc site and I am using LinFu to do IOC. I ran into an issue where a number of actions have a dependency that I want to inject into the controller but i only want to initialize the dependency if i call the action that depends on it.

so in my controller I have this code in my controller:

   public PersonController
   {

    private IPeopleImporter _peopleImporter;

    public override void Initialize(LinFu.IoC.Interfaces.IServiceContainer source)
    {
        _peopleImporter= source.GetService<IPeopleImporter>();
        base.Initialize(source);
    }

    public JsonResult GetDetails(int id)
    {
        var p = _peopleImporter.Get(id);
        var personDetails = new {p.Id, p.FirstName, p.LastName, StandardId = p.StandardIdLogin, p.PersonNumber};
        return Json(personDetails);
    }
   }

to initiatize PeopleImporter is pretty expensive so my issue is that I want to solve two things:

  1. I want to make the implementatioo of IPeopleImporter "pluggable" so I can IOC in the interface into the controller

  2. I have a lot of actions so I don't want the cost of initiatizing the IPeopleImporter if the user never calls the specific action that needs it. It seems like in the code above I do that initiatization on every call of PersonController

My initiatization code is like this:

this.AddService(typeof(IPeopleImporter), typeof(DatabaseImporter), LifecycleType.Singleton);

This seems like common pattern / issue. Is there a recommended solution. in the meantime, the alternative (to avoid the performance hit is to simple "new" up the concete implmentation inside the controller (and avoid IOC) ?

leora
  • 188,729
  • 360
  • 878
  • 1,366
  • I don't know about LinFu but many other IOC frameworks provide factory injection which can be used to instantiate the instance when you need it. – Eranga Jul 25 '12 at 03:47
  • What you have shown is not IoC. You must be confusing the terms here. In IoC, the controller takes the dependency as an constructor argument. In your example you are tying your controller to the DI framework (LinFu in your case) and you are using the Service Locator pattern (In the Initialize method you are querying your DI framework to retrieve dependencies). This is considered as an anti-pattern. – Darin Dimitrov Jul 27 '12 at 13:08

3 Answers3

3

The first step would be into putting this action into a separate controller. So that you don't have to pay the initialization price for other actions in this controller.

Then use real IoC pattern, not Service Locator that you are currently using which is considered as an anti-pattern. In IoC the controller shouldn't know anything about the specific DI framework being used. In IoC the controller receives all dependencies as constructor arguments

public PersonsController: Controller
{
    private readonly IPeopleImporter _peopleImporter;
    public PersonsController(IPeopleImporter peopleImporter)
    {
        _peopleImporter = peopleImporter;
    }

    public ActionResult GetDetails(int id)
    {
        var p = _peopleImporter.Get(id);
        var personDetails = new { p.Id, p.FirstName, p.LastName, StandardId = p.StandardIdLogin, p.PersonNumber };
        return Json(personDetails, JsonRequestBehavior.AllowGet);
    }
}
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • in IoC you can have your dependencies injected by other means than the constructor. Sure the constructor is the most common but not the sole possible mechanism – Rune FS Jul 30 '12 at 07:43
  • @RuneFS, yes, that's right. You could use property injection for non-required dependencies. – Darin Dimitrov Jul 30 '12 at 07:59
  • @Darin - I agree with your point . . looking to migrate off Linfu at this point given it doesn't seem to support proper IOC where the controllers don't need a reference to the IOC Container. – leora Jul 30 '12 at 16:47
1

You can use next trick for your task. You can inject not instance of IPeopleImporter but factory of this type:

private readonly Func<IPeopleImporter> _peopleImporterFactory;

and use this factory in your action where you need it:

var peopleImporter = __peopleImporterFactory();

example:

 public PersonController

{

private readonly Func<IPeopleImporter> _peopleImporterFactory;

public PersonController(Func<IPeopleImporter> peopleImporterFactory)
{
    _peopleImporterFactory = peopleImporterFactory;
}

public JsonResult GetDetails(int id)
{
    var peopleImporter = _peopleImporterFactory();
    var p = peopleImporter.Get(id);
    var personDetails = new {p.Id, p.FirstName, p.LastName, StandardId = p.StandardIdLogin, p.PersonNumber};
    return Json(personDetails);
}

}

Kirill Bestemyanov
  • 11,946
  • 2
  • 24
  • 38
0

I usually solve this by injecting dependencies as parameters to my actions. Common or cheap dependencies can be injected at the class level while unusual or expensive ones come in as parameters.

Here is some sample code for Enterprise Library Unity, but you can adapt it:

public class UnityActionInvoker : ControllerActionInvoker
{
    readonly IUnityContainer container;

    public UnityActionInvoker(IUnityContainer container)
    {
        this.container = container;
    }

    protected override object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
    {
        Type parameterType = parameterDescriptor.ParameterType;

        if (parameterType != typeof(string) && !parameterType.IsValueType && container.IsRegistered(parameterType))
        {
            return container.Resolve(parameterType).AssertNotNull();
        }

        return base.GetParameterValue(controllerContext, parameterDescriptor);
    }
}

You can hook this ControllerActionInvoker in by setting Controller.ActionInvoker in the constructor of your controller (or in the common base class of all your controllers).

And here is how your controller might look like:

   public PersonController
   {
    public JsonResult GetDetails(int id, IPeopleImporter _peopleImporter /*injected*/)
    {
        ...
    }
   }
usr
  • 168,620
  • 35
  • 240
  • 369