This blog post will show you how I setup Simple Injector Dependency Injection in Visual Studio 2015 MVC solution for Sitecore 8.1. I use Simple Injector a lot and is my favorite. I have never had performance issues with it. Hope you enjoy it too.
You can get more information about Simple Injector here.
TIHIDI: Stands for This Is How I Do It. I am going to write a series of blog posts going through how I do Sitecore related work. Hope it helps you!
One of the best ways to install any packages in Visual Studio is to use NuGet Package Manager Console available under the Tools > NuGet Package Manager option. There is a UI available but using the console is the best way for me.
If you are unsure of the command to use, visit www.nuget.org and search for Simple Injector.
Click on the search result and it will show you information on the package.
Copy the command, paste it in your Visual Studio Package Manage Console window and hit the Enter key.
Now that we have Simple Injector installed, we need to plug it into Sitecore.
In order to help us with Simple Injector you would also need to install SimpleInjector.Integration.Mvc and SimpleInjector.Packing from NuGet.
We will first define the Controller Factory. To do this, we will create a new project in our solution called SCExtensions to house all the Sitecore customizations.
Add a new Class project to your solution and name it TIHIDI.SCExtensions.
Create the appropriate folder structure and add a class called SimpleInjectorControllerFactory.
Install Simple Injector using NuGet in this project.
Copy the System.Web.MVC from your website bin folder and dump it in to the Dependencies\Sitecore folder.
Reference the above System.Web.MVC as well as System.Web and System.Routing in the TIHIDI.SCExtensions project.
Also add in Sitecore.Kernel and Sitecore.Mvc references in the SCExtensions project.
Here is the code for the SimpleInjectorControllerFactory class.
using SimpleInjector; using System; using System.Web.Mvc; using System.Web.Routing; namespace TIHIDI.SCExtensions.DI { public class SimpleInjectorControllerFactory : DefaultControllerFactory { private Container _container; public SimpleInjectorControllerFactory(Container container) { _container = container; } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { return (IController)_container.GetInstance(controllerType); } } }
Now we need to initialize this. There are several ways to initialize Simple Injector, you could use Global.asax or WeActivator but staying true to Sitecore, the best way to do it is to use the Initialize pipeline.
I also want to have access to the ContainerManager for future registration especially when I add Glass models.
Create a new class in the SCExtensions project called ContainerManager. Here is the code for the class.
using SimpleInjector; namespace TIHIDI.SCExtensions.DI { public class ContainerManager { private static readonly Container container = new Container(); public Container Container { get { return container; } } } }
We also want to be able to do Property Injection, create a new class ImportAttributePropertySelectionBehavior in the SCExtensions project. Here is the code for this class.
using SimpleInjector.Advanced; using System; using System.ComponentModel.Composition; using System.Linq; using System.Reflection; namespace TIHIDI.SCExtensions.DI { public class ImportAttributePropertySelectionBehavior : IPropertySelectionBehavior { public bool SelectProperty(Type serviceType, PropertyInfo propertyInfo) { return propertyInfo.GetCustomAttributes(typeof(ImportAttribute), true).Any(); } } }
Create a new class in the SCExtensions project called InitializeSimpleInjectorControllerFactory. Here is the code to do the initialization:
using SimpleInjector; using Sitecore.Pipelines; using System; using System.Linq; using System.Web.Mvc; using SimpleInjector.Integration.Web.Mvc; using SimpleInjector.Integration.Web; namespace TIHIDI.SCExtensions.DI.Pipelines { public class InitializeSimpleInjectorControllerFactory { public virtual void Process(PipelineArgs args) { SetControllerFactory(args); } private void SetControllerFactory(PipelineArgs args) { ContainerManager containerM = new ContainerManager(); SetContainerOptions(containerM.Container); // you can customize this to what ever you need. This registers all IPackage implementations PackageExtensions.RegisterPackages(containerM.Container, AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName.StartsWith("TIHIDI."))); DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(containerM.Container)); } private void SetContainerOptions(Container container) { container.Options.PropertySelectionBehavior = new ImportAttributePropertySelectionBehavior(); container.Options.DefaultScopedLifestyle = new WebRequestLifestyle(); container.RegisterMvcIntegratedFilterProvider(); //container.Options.AllowOverridingRegistrations = true; //we do not want overriding registrations but if you do this is the property you set } } }
Since we have the code in place to pick up IPackage implementations, lets create a class called SimpleInjector.cs in the App_Start folder to add our registrations. Here is he sample code:
using SimpleInjector.Packaging; using System; using SimpleInjector; using Glass.Mapper.Sc; using System.Reflection; using System.Collections.Generic; using Glass.Mapper.Maps; using System.Linq; using TIHIDI.SCExtensions.DI; using TIHIDI.Business.GlassSC; using TIHIDI.Business.Content; namespace TIHIDI.Web.App_Start { public class SimpleInjector:IPackage { public void RegisterServices(Container container) { container.Register(() => new SitecoreContext()); } } }
Now that we have all the plumbing necessary, we need to register this pipeline processor using a custom config in the App_Config > Include folder.
Now let’s build and deploy this solution. The site loads and we can see our config patch in the showconfig.aspx
If you have any questions or concerns, please get in touch with me. (@akshaysura13 on twitter or on Slack).
UPDATE (7/21/2016): Following Corey Smith‘s comment, I went ahead and implemented the code I usually do. To my surprise it did not work, when I looked into it, I realized that all this while I have been working with Simple Injector versions lower than 3.0. As of Simple Injector 3.0 and above there have been a few API changes which I needed to make. I went ahead and make all appropriate changes for Simple Injector version 3.2. I also looked into my initialization code and realized that it was legacy code from when I started using Ninject a while back. Thanks Corey.
Nice post Akshay. Why do you wrap your Container in a ControllerFactory instead of setting your Container as the MVC DependencyResolver?
Corey not sure if I understand but are you talking about the following?
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(containerM.Container));
Exactly.
haha this is what happens when you manipulate code in wordpress 😉
This is really good. In my earlier project we have been used CastleWindsor.DependencyResolver. What is the pros and cons between Castle.Windsor and SimpleInjector.
Night and day. Castle is a lot slower than SimpleInjector. For other features and comparison, please refer to the following links:
http://www.palmmedia.de/blog/2011/8/30/ioc-container-benchmark-performance-comparison
https://cardinalcore.co.uk/2015/01/28/ioc-battle-in-2015-results-using-ninject-think-again/
How to integrate it to have WebAPI as well?
Afshin,
You would need separate registrations for WebAPI calls. I do not have the code off hand but it should be available if you search on Google.
Below line is throwing error:
container.Register(() => new SitecoreContext());
By default, the value of Lifestyle is Transient. To fix this issue we should use Lifestyle.Scoped like
SIContainer.Register(() => new SitecoreContext(),SI.Lifestyle.Scoped);