Reputation: 3705
I've been using Ninject for a while, but only for simple DI. I have a slightly more complex scenario that I'd like to cater for.
I'm using the Ninject.Web.MVC plugin with controller injection, which is forced on my a base controller, passed through to all of the inheriting controllers. Here's my basic pattern:
using System;
using System.Web;
using System.Web.Mvc;
using Business;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Common;
namespace Web {
#region Controller
public class MyController : MyBaseController {
public MyController(IMyService1 myService1, IMyService2 myService2)
: base(myService1, myService2) {
}
public ActionResult MyAction() {
MyServiceProp1.DoSomething();
MyServiceProp2.DoSomethingElse();
return View();
}
}
public class MyBaseController : Controller {
protected IMyService1 MyServiceProp1 { get; set; }
protected IMyService2 MyServiceProp2 { get; set; }
public MyBaseController(IMyService1 myService1, IMyService2 myService2) {
MyServiceProp1 = myService1;
MyServiceProp2 = myService2;
}
}
#endregion
}
namespace Business {
#region Services
public interface IMyService1 {
void DoSomething();
}
public interface IMyService2 {
void DoSomethingElse();
}
public class MyService1 : IMyService1 {
public void DoSomething() {
}
}
public class MyService2 : IMyService2 {
public void DoSomethingElse() {
}
}
#endregion
}
#region Ninject stuff
namespace Web.App_Start {
public static class NinjectWebCommon {
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start() {
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop() {
bootstrapper.ShutDown();
}
/// <summary>
/// Creates a kernel that will manage the application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel() {
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
return kernel;
}
/// <summary>
/// Loads modules and services
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel) {
kernel.Bind<IMyService1>().To<MyService1>();
kernel.Bind<IMyService2>().To<MyService2>();
}
}
}
#endregion
As you can see, I have multiple services. If I want to add MyService3, I will have to modify every single controller that's implemented the base controller. Whilst this isn't a massive job as it stands, it will be when the project is larger...
What I would like to do is implement some kind of service container, which I will inject in to the base controller, and therefore all of the implementing controllers (and any subsequent classes):
namespace Web {
#region Controller
public class MyController : MyBaseController {
public MyController(IMyServiceContainer container)
: base(container) {
}
public ActionResult MyAction() {
MyServiceProp1.DoSomething();
MyServiceProp2.DoSomethingElse();
return View();
}
}
public interface IMyServiceContainer {
IMyService1 MyServiceProp1 { get; set; }
IMyService2 MyServiceProp2 { get; set; }
}
public class MyBaseController : Controller, IMyServiceContainer {
public MyBaseController(IMyServiceContainer container) {
MyServiceProp1 = container.MyServiceProp1;
MyServiceProp2 = container.MyServiceProp2;
}
#region Implementation of IMyServiceContainer
public IMyService1 MyServiceProp1 { get; set; }
public IMyService2 MyServiceProp2 { get; set; }
#endregion
}
#endregion
}
This way, if I need to add a global dependency, I won't have to change any of the controllers, just the base controller, which is much easier to maintain.
Is there a way to achieve this with Ninject (or even Unity, as I will be changing it over to that shortly), and does the pattern / method have a specific name that I've not yet heard?
Upvotes: 2
Views: 786
Reputation: 1039398
public interface IMyServiceContainer
{
IMyService1 MyService1 { get; }
IMyService2 MyService2 { get; }
IMyService3 MyService3 { get; }
}
public class MyServiceContainer
{
public IMyService1 MyService1 { get; private set; }
public IMyService2 MyService2 { get; private set; }
public IMyService3 MyService3 { get; private set; }
public MyServiceContainer(IMyService1 myService1, IMyService2 myService2, IMyService3 myService3)
{
MyService1 = myService1;
MyService2 = myService2;
MyService3 = myService3;
}
}
and then your base controller:
public class MyBaseController : Controller
{
public MyBaseController(IMyServiceContainer container)
{
Container = container;
}
protected IMyServiceContainer Container { get; private set; }
}
and then the derived controller:
public class MyController : MyBaseController
{
public MyController(IMyServiceContainer container) : base(container)
{
}
public ActionResult MyAction()
{
Container.MyService1.DoSomething();
Container.MyService2.DoSomethingElse();
return View();
}
}
and now all that's left is to configure NInject:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IMyService1>().To<MyService1>();
kernel.Bind<IMyService2>().To<MyService2>();
kernel.Bind<IMyService3>().To<MyService3>();
kernel.Bind<IMyServiceContainer>().To<MyServiceContainer>();
}
Upvotes: 3