Dependency Injection in SharePoint with Ninject

 

A while ago I was having a discussion with one of my fellow developers at EMC Consulting about SharePoint and getting dependency injection working with it. Well finally after a few weeks I have found the time to produce a quick example of just how easy it is to get dependency injection working in SharePoint using a great tool called Ninject. You can find additional information about Ninject at http://ninject.org/. So this get started!
First off Ninject works by providing you with something it calls a kernel which it uses for returning an instance of a specific type. You can think about the kernel as a big brother to the factory pattern we all know and love. Because the kernel is so important the first things you need to do to get Ninject working in SharePoint is create an instance of a kernel and a simple way to do this in SharePoint is through the use of a HttpModule.

1:  public class SharePointNinjectHttpModule: IHttpModule, IDisposable
2:  {
3:         private readonly HttpApplication _httpApplication;
4: 
5:         public void Init(HttpApplication context)
6:         {
7:             if (context == null)
8:                 throw new ArgumentException("context");
9: 
10:             if(FrameworkHelper.Kernel == null)
11:             {
12:                 FrameworkHelper.Kernel = GetKernel();
13:             }
14:         }
15: 
16:         public void Dispose()
17:         {
18:             if(_httpApplication == null) return;
19:             _httpApplication.Dispose();
20:         }
21: 
22:         #region Private methods
23:         
24:         /// <summary>
25:         /// Gets the kernel.
26:         /// </summary>
27:         /// <returns></returns>
28:         private static IKernel GetKernel()
29:         {           
30:             IKernel result = new StandardKernel();
31:             result.Bind<IWarrior>().To<Samurai>();
32:             result.Bind<IWeapon>().To<Sword>();
33:             return result;
34:         }
35: 
36:         #endregion
37: }



As you can see from the code the module is very simple and straightforward and all it does is create an instance of the kernel and sets it's bindings so Ninject knows which instance of a type to return when a request for “IWarrior” or “IWeapon” is made. Remember that you will need to add an entry to the “httpModules” section of your web.config (see below) which in a real world application would be done using the SPWebConfigModification class. However, as this is just a demo I have added it by hand to save myself sometime.

1: <add name="SharePointNinjectHttpModule" type="Blog.IOC.Source.HttpModules.SharePointNinjectHttpModule,Blog.IOC, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bf2ebb07a9bc44ba" />



Now to allow Ninject to actually do it's dependency injection magic there are a few things you must do. The first of these is to modify the “AssemblyInfo.cs” file for all of your projects (well the ones using Ninject at least) so they will allow partially trusted callers. If you don't do this then a security exception is thrown when you inject your object into the Ninject kernel.

1: using System.Security;
2: 
3: [assembly: AllowPartiallyTrustedCallers]



The next thing you need to do is inject the actual instance of the object you want Ninject to apply dependency injection to into the kernel you created. The way I did this was to create a number of base classes which inherit from commonly used items in SharePoint like Web Parts, User Controls and Application Pages. Then within the constructor for each base class I inject the instance of that object into the Ninject kernel.


This is the web part base class:

1: public abstract class WebPartBase : Microsoft.SharePoint.WebPartPages.WebPart
2: {
3:       protected WebPartBase()
4:       {
5:           FrameworkHelper.Kernel.Inject(this);
6:       }
7: }



This is the application page base class:

1: public class LayoutsBase : LayoutsPageBase
2: {
3:         protected LayoutsBase()
4:         {
5:             FrameworkHelper.Kernel.Inject(this);
6:         }
7: }



And finally the user control base class:

1: public abstract class UserControlBase : UserControl
2: {
3:         protected UserControlBase()
4:         {
5:             FrameworkHelper.Kernel.Inject(this);
6:         }
7: }



You can see all the bases classes shown below in the “Blog.IOC” project.

Blog.IOC.BaseClasses


Once you have done this Ninject will work and happily inject any dependencies you have marked up with the “[Inject]” attribute into your objects.

1: [Inject]
2: public IWarrior Warrior { get; set; }


So does this all work you ask? Well yes indeed it does and to prove it below is the code for a very simple application page which inherits off “LayoutsBase” and a screen shot of it’s rendered output.


[ Notice the use of the inject attribute on the Warrior property so Ninject knows this is something it is responsible for ]

1: public class BlogCustomApplicationPage : LayoutsBase
2: {
3:         protected Literal litWarrior;
4: 
5:         [Inject]
6:         public IWarrior Warrior { get; set; }
7: 
8:         protected override void OnLoad(EventArgs e)
9:         {
10:             base.OnLoad(e);
11: 
12:             SetUpWarrior();
13:         }
14: 
15:         private void SetUpWarrior()
16:         {
17:             litWarrior.Text = Warrior.Name;
18:         }
19: }



[ The fully rendered output ]
ApplicationPage


The full source code for the dependency injection example using Ninject can be download from: http://cid-468e9f9e14e99f80.skydrive.live.com/self.aspx/.Public/Blog.IOC.zip

PS. In order to get all this working you will need Visual Studio 2008, WSPBuilder and SharePoint 2007 enjoy :-)

MOSS: Error "The handle is invalid" when starting Windows SharePoint Services (WSS) Search service

If you attempt to start Windows SharePoint Services Search service and receive the following message:

wss-service-error

Try modifying the Log On Identity of the service.  In my case, it was setup to log on as "Local Service".  After I changed this to "Local System", the service started without any errors.

Note that this error message was received from the Services Administrative Tool.  The WSS web front-end simply hung when attempting to start the WSS Search service.

February 16 2007

MOSS: Custom commands for the stsadm.exe utility

I'm doing some work with Microsoft Office SharePoint Server (MOSS) and I recently discovered that with MOSS (and WSS v3) the administration utility is extensible.  Stsadm.exe is something that I have a love-hate relationship with, but it's interesting that it's commands work on a provider model.  Tony Bierman has posted all the details on the SharePoint Solutions Blog and Andrew Connell has some real-world examples of custom commands.

I can see these custom commands being really useful in many MOSS deployment scenarios - anyone who has ever tried to write an MSI or a batch file deployment for a SharePoint site will know how invaluable stsadm.exe really is.  Of course it does raise a bootstrapping issue, in that you have to get your custom commands deployed before you can use them for the rest of your deployment.  However, this shouldn't be an insurmountable problem.

January 22 2007

MOSS: Programmatically modifying a shared webpart

On my current project, we create SharePoint sites in response to calls to a particular webservice and there is one site per domain entity.  In this webservice, there is a certain amount of customisation of the site homepage which needs to be done.  In particular, we have an RSS Aggregator web part and we want this to point to a different URL for each site.

I couldn't find anywhere on the web that had a sample of doing this, so I thought I'd blog the solution:

// Reconfigure the RSS Reader to query against the company name

SPLimitedWebPartManager WPMgr = NewSite.GetLimitedWebPartManager("default.aspx", PersonalizationScope.Shared);

foreach (WebPart wp in WPMgr.WebParts)

{

    RSSAggregatorWebPart RssPart = wp as RSSAggregatorWebPart;

    if (RssPart != null)

    {

        string QueryString = HttpContext.Current.Server.UrlEncode(person.Company);

        RssPart.FeedUrl = RssPart.FeedUrl.Replace("company", QueryString);

        WPMgr.SaveChanges(RssPart);

    }

}

 

Clearly NewSite and person.Company are defined higher up in my code, but you can do whatever you want with the FeedUrl property.  To get this to work you need to have references to:

  • System.Web [for System.Web.WebPart and System.Web.UI.WebControls.WebParts.PersonalizationScope]
  • Microsoft.SharePoint [for Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager]
  • Microsoft.SharePoint.Portal [for Microsoft.SharePoint.Portal.WebControls.RSSAggregatorWebPart]

The list above also shows which namespaces I import through using declarations.

PS. Apologies to anyone who's disappointed at the lack of WPF/E content in my latest posts.  I hope to play some more with that technology soon, but there'll probably be a pre-dominance of MOSS posts in the coming months.  If you are really uninterested in MOSS posts, then make sure you only subscribe to a tagged feed and not to the main one.

January 22 2007
Older Posts