I have successfully implemented an enterprise SharePoint solution using Ninject dependency injection and other infrastructure such as NLog logging etc using an Onion architecture. With a HttpModule as an Composition Root for the injection framework, it works great for normal web requests:
public class SharePointNinjectHttpModule: IHttpModule, IDisposable
{
private readonly HttpApplication _httpApplication;
public void Init(HttpApplication context)
{
if (context == null) throw new ArgumentException("context");
Ioc.Container = IocContainerFactory.CreateContainer();
}
public void Dispose()
{
if(_httpApplication == null) return;
_httpApplication.Dispose();
Ioc.Container.Dispose();
}
}
The CreateContainer method loads the Ninject modules from a separate class library and my ioc container is abstracted. For normal web application requests I used a shared static class for the injector called Ioc. The UI layer has a MVP pattern implementation. E.g in the aspx page the presenter is constructed as follows:
presenter = Ioc.Container.Get<SPPresenter>(new Ninject.Parameters.ConstructorArgument("view", this));
I'm still reliant on a Ninject reference for the parameters. Is there any way to abstract this, other than mapping a lot of methods in a interface? Can't I just pass in simple types for arguments?
The injection itself works great, however my difficulty comes in when using external processes such as SharePoint Timer Jobs. It would obviously be a terrible idea to reuse the ioc container from here, so it needs to bootstrap the dependencies itself. In addition, it needs to load the configuration from the web application pool, not the admin web application. Else the job would only be able to run on the application server. This way the job can run on any web server, and your SharePoint feature only has to deploy configurations etc. to the web apllication.
Here is the execute method of my timer job, it opens the associated web application configuration and passes it to the logging service (nlog) and reads it's configuration from the external web config service. I have written code that reads a custom section in the configuration file and initializes the NLog logging infrastructure.
public override void Execute(Guid contentDbId)
{
try
{
using (var ioc = IocContainerFactory.CreateContainer())
{
// open configuration from web application
var configService = ioc.Get<IConfigService>(new ConstructorArgument("webApplicationName", this.WebApplication.Name));
// get logging service and set with web application configuration
var logginService = ioc.Get<ILoggingService>();
logginService.SetConfiguration(configService);
// reapply bindings
ioc.Rebind<IConfigService>().ToConstant(configService);
ioc.Rebind<ILoggingService>().ToConstant(logginService);
try
{
logginService.Info("Test Job started.");
// use services etc...
var productService = ioc.Get<IProductService>();
var products = productService.GetProducts(5);
logginService.Info("Got products: " + products.Count() + " Config from web application: " + configService.TestConfigSetting);
logginService.Info("Test Job completed.");
}
catch (Exception exception)
{
logginService.Error(exception);
}
}
}
catch (Exception exception)
{
EventLog.WriteError(exception, "Exception thrown in Test Job.");
}
}
This does not make the timer jobs robust enough, and there is a lot of boiler plate code. My question is how do I improve on this design? It's not the most elegant, I'm looking for a way to abstract the timer job operation code and have it's dependencies injected into it for each timer job. I would just like to hear your comments if you think this is a good approach. Or if someone has faced similar problems like this? Thanks