Archive

Archive for September, 2012

Sitecore MVC Ninject controller

17/09/2012 7 comments

This is last post in the series of three. As promised in my last post we will inject a repository through the construct in the controller. To do this we will use the Ninject as our IOC container, This will be a very quick walkthrough.

Also we to make it a bit easier to find controller that needs to have class’ injected we will create a derived class from the controller class’,

    public class NinjectController : Controller
    {}

Some of the assmeblies in sitecore mvc solution gives and error when trying to reflect upon them so there are a couple of catch’s in the code to handle some minor bugs.

Next we will create our controller factory  wrapping the Sitecores standard factory. This new Ninject controller factory will for the controller that should be loaded is of type nijectcontroller instantiate the corresponding concrete type of the constructers parameters.. Also this class loads all the ninject binding. Maybe it’s not so nice that it has multiple responsibility but it is left out for an exercise to refactor the code so it conforms to SRP 🙂 .

public class NinjectInitializeControllerFacotry
{
  public virtual void Process(PipelineArgs args)
  {
    this.SetControllerFactory(args);
  }

  protected virtual void SetControllerFactory(PipelineArgs args)
  {
    NinjectControllerFactory controllerFactory = new     NinjectControllerFactory(ControllerBuilder.Current.GetControllerFactory());
    ControllerBuilder.Current.SetControllerFactory(controllerFactory);
  }
}

and the controller factory it self

  public class NinjectControllerFactory : SitecoreControllerFactory
  {
    public NinjectControllerFactory(IControllerFactory innerFactory) : base(innerFactory)
    {
    }

    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
      Assert.ArgumentNotNull(requestContext, "requestContext");
      Assert.ArgumentNotNull(controllerName, "controllerName");
      try
      {
         IController controller = ICreateController(requestContext, controllerName);
         return controller;
      }
      catch (Exception exception)
      {
        if (!MvcSettings.DetailedErrorOnMissingController)
        {
          throw;
        }
        return new ErrorHandlingController(controllerName, exception);
     }
   }

   public override void ReleaseController(IController controller)
   {
    var disposable = controller as IDisposable;

    if (disposable != null)
    {
      disposable.Dispose();
    }
  }

  private IController ICreateController(RequestContext requestContext, string controllerName)
  {
    string fullControllerName = string.Format("{0}Controller", controllerName);
    Type ninjectController = FindSpecificNinjectController(fullControllerName);
    if (ninjectController != null)
    {
      return (IController)Kernel.Get(ninjectController);
    }
    return base.CreateController(requestContext, controllerName);
  }

  private Type FindSpecificNinjectController(string controllerName)
  {
    IEnumerable<Type> ninjectControllers = GetNinjectControllers();
    if(ninjectControllers.Any())
    {
     Type selectedNincjectController = ninjectControllers.FirstOrDefault(controller => controller.Name == controllerName);
     if (selectedNincjectController != null)
     return selectedNincjectController;
   }
   return null;
  }

 private IEnumerable<Type> GetNinjectControllers()
 {
    List<Type> types = new List<Type>();
    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
    types.AddRange(FindDerivedTypes(assembly, typeof(NinjectController)));
    return types;
 }
 private IEnumerable<Type> FindDerivedTypes(Assembly assembly, Type baseType)
 {
   try
   {
    return assembly.GetTypes().Where(t => baseType.IsAssignableFrom(t));
   }
   catch (Exception)
   {
    return new List<Type>();
 }
 }

  private static IKernel _kernel;
  private IKernel Kernel
  {
   get
   {
    if (_kernel == null)
      CreateKernel();
     return _kernel;
    }
 }

protected void CreateKernel()
 {
   _kernel = new StandardKernel();
    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
   {
    if (HasNinjectModules(assembly))
    _kernel.Load(assembly);
  }
 }

public static bool HasNinjectModules(Assembly assembly)
 {
   Type baseType = typeof (NinjectModule);
   try
   {
    return assembly.GetTypes().Any(t => baseType.IsAssignableFrom(t));
   }
  catch (Exception)
  {
    return false;
  }
  }
}

So all that is left is to overwrite the standard controller factory.


<initialize>
 <processor type="SitecorePresentation.SitecoreMVCRoutes,SitecorePresentation">
 <mvcIgnoreHomePage>false</mvcIgnoreHomePage>
 </processor>
 <processor type="Sitecore.Mvc.Pipelines.Loader.InitializeGlobalFilters, Sitecore.Mvc"/>
 <processor type="SitecoreMVCNinjectExtension.NinjectInitializeControllerFacotry, SitecoreMVCNinjectExtension"/>
 <!--<processor type="Sitecore.Mvc.Pipelines.Loader.InitializeControllerFactory, Sitecore.Mvc"/>-->
 <processor type="Sitecore.Mvc.Pipelines.Loader.InitializeRoutes, Sitecore.Mvc"/>
 </initialize>

Next we can rewrite the controller from the last episode to conform to the new ninjectcontroller


public class NinjectItemController : NinjectController
 {
 private readonly IChildItemRepository _childItemRepository;
 public NinjectItemController(IChildItemRepository childItemRepository)
 {
 _childItemRepository = childItemRepository;
 }

public JsonResult Index(string id)
 {
 return Json(CreateObject(_childItemRepository.Get(id)), JsonRequestBehavior.AllowGet);
 }

&nbsp;

private IEnumerable<object> CreateObject(IEnumerable<Item> items)
 {
 List<object> objectList = new List<object>();
 foreach (Item item in items)
 objectList.Add(new { item.Name });
 return objectList;
 }

}

The repositories from the last post looks the samen but are listed below.

</pre>
public interface IChildItemRepository
{
IEnumerable<Item> Get(string parentItemId);
}
<pre>
</pre>
public class ChildItemRepository : IChildItemRepository
{
public virtual IEnumerable<Item> Get(string parentItemId)
{
ID parentId;
if(ID.TryParse(parentItemId,out parentId))
return GetChildNodes(parentId);
return new List<Item>();
}

private IEnumerable<Item> GetChildNodes(ID parentId)
{
Item parentItem = Sitecore.Context.Database.GetItem(parentId);
return parentItem.GetChildren();
}
}

Now to ninject magic the ninject module that factory are loading bindings from.


public class ChildItemRepositoryNinjectModule : NinjectModule
{
public override void Load()
{
Bind<IChildItemRepository>().To<ChildItemRepository>();

}
}

And of course now we can rewrite out unittest to test the new controller.


public class ItemControllerTest
 {
 [Test]
 public void ItemControllWithNoIdShouldReturnEmptyList()
 {
 IChildItemRepository childItemRepositoryStub = Substitute.For<IChildItemRepository>();
 childItemRepositoryStub.Get(Arg.Any<string>()).Returns(new List<Item>());
 NinjectItemController controller = new NinjectItemController(childItemRepositoryStub);

 JsonResult result = controller.Index(string.Empty);
 Assert.That(result.Data,Is.Empty);
 }

[Test]
 public void ItemControllWithInvalideIDShouldReturnEmptyList()
 {

IChildItemRepository childItemRepositoryStub = Substitute.For<IChildItemRepository>();
 childItemRepositoryStub.Get(Arg.Any<string>()).Returns(new List<Item>());
 NinjectItemController controller = new NinjectItemController(childItemRepositoryStub);
 JsonResult result = controller.Index("invalidID");
 Assert.That(result.Data, Is.Empty);
 }

[Test]
 public void ItemControllWithValidIdShouldReturnJsonListWithItemNames()
 {
 IChildItemRepository childItemRepositoryStub = Substitute.For<IChildItemRepository>();
 List<Item> childList = new List<Item>();

 childList.Add(new ItemStub("stub-a"));
 childList.Add(new ItemStub("stub-b"));
 childList.Add(new ItemStub("stub-c"));
 childList.Add(new ItemStub("stub-d"));

Guid itemGuid = Guid.NewGuid();
 childItemRepositoryStub.Get(itemGuid.ToString()).Returns(childList);
 NinjectItemController controller = new NinjectItemController(childItemRepositoryStub);
 JsonResult result = controller.Index(itemGuid.ToString());

List<object>resultData = (List<object>) result.Data; ;
 Assert.AreEqual(4,resultData.Count());
 Assert.AreEqual(resultData[0].ToString(),"{ Name = stub-a }");
 Assert.AreEqual(resultData[1].ToString(), "{ Name = stub-b }");
 Assert.AreEqual(resultData[2].ToString(), "{ Name = stub-c }");
 Assert.AreEqual(resultData[3].ToString(), "{ Name = stub-d }");
 }
 }

The import thing about the rewrite of the test are the ugly SET on the itemctroller is removed as the repository is passed in as contructor argument.The result from the test

As a last service the image below shows the file structure in visual studio .

Thats all, now we can create easy to test controllers using a repository pattern.

Advertisements