I've been following the steps in the 'Microsoft Enterprise Library 5.0' documentation to create a HTTP module to inject a reference to the Enterprise Library container into the pages of ASP.NET web application.
It contains the following code (which also appears online here):
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using Microsoft.Practices.Unity;
namespace Unity.Web
{
public class UnityHttpModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += OnPreRequestHandlerExecute;
}
public void Dispose() { }
private void OnPreRequestHandlerExecute(object sender, EventArgs e)
{
IHttpHandler currentHandler = HttpContext.Current.Handler;
HttpContext.Current.Application.GetContainer().BuildUp(
currentHandler.GetType(), currentHandler);
// User Controls are ready to be built up after page initialization is complete
var currentPage = HttpContext.Current.Handler as Page;
if (currentPage != null)
{
currentPage.InitComplete += OnPageInitComplete;
}
}
// Build up each control in the page's control tree
private void OnPageInitComplete(object sender, EventArgs e)
{
var currentPage = (Page)sender;
IUnityContainer container = HttpContext.Current.Application.GetContainer();
foreach (Control c in GetControlTree(currentPage))
{
container.BuildUp(c.GetType(), c);
}
context.PreRequestHandlerExecute -= OnPreRequestHandlerExecute;
}
// Get the controls in the page's control tree excluding the page itself
private IEnumerable<Control> GetControlTree(Control root)
{
foreach (Control child in root.Controls)
{
yield return child;
foreach (Control c in GetControlTree(child))
{
yield return c;
}
}
}
}
}
There are a number of problems with this code and the instructions that came with it.
1) The instructions don't mention where to place this code. Since it is a class, I placed it in the App_Code folder of my ASP.NET website project.
In fact, here is the instructions for this bit of code:
Create a new ASP.NET HTTP module class (named, for example, UnityHttpModule ) in your project that captures the PreRequestHandlerExecute event and executes code that walks the complete control tree of the current page request, applying the Unity BuildUp method to each control.
2) The HttpContext.Current.Application.GetContainer() method does not exist for me, even though I have the same DLL references used (I'm coding in .NET 4.0).
3) The OnPageInitComplete event references a 'context' variable... which doesn't seem to exist in this context.
Any ideas on what I'm missing here?
Seems the documentation is badly organised.
In response to (2), what wasn't explained is that the HttpContext.Current.Application.GetContainer() method is actually an extension method, which is implemented like the code shown here
.
To use this extension method, you simply have to import the 'Unity.Web' namespace.
Here is a copy of the extension method:
using System.Web;
using Microsoft.Practices.Unity;
namespace Unity.Web
{
public static class HttpApplicationStateExtensions
{
private const string GlobalContainerKey = "EntLibContainer";
public static IUnityContainer GetContainer(this HttpApplicationState appState)
{
appState.Lock();
try
{
var myContainer = appState[GlobalContainerKey] as IUnityContainer;
if (myContainer == null)
{
myContainer = new UnityContainer();
appState[GlobalContainerKey] = myContainer;
}
return myContainer;
}
finally
{
appState.UnLock();
}
}
}
}
With regards to the the dependency injection module code, I actually just used the basic method for getting an instance of the container, which as far as I'm concerned works just as well. The documentation says that the dependency injection HTTP module code improves 'testability' and 'discoverability', which is a little vague.
Anyway, here's the code for the basic approach:
protected void Application_Start(object sender, EventArgs e)
{
Application.Lock();
try
{
var myContainer = Application["EntLibContainer"] as IUnityContainer;
if (myContainer == null)
{
myContainer = new UnityContainer();
myContainer.AddExtension(new EnterpriseLibraryCoreExtension());
// Add your own custom registrations and mappings here as required
Application["EntLibContainer"] = myContainer;
}
}
finally
{
Application.UnLock();
}
}
So with the extension code in place, and code in my global.asax file to create an instance of the Enterprise Library container, the only thing left to do is write the code to get an instance of the container as needed. So when I want to get an instance of the LogWriter class, I would write this:
using Unity.Web;
public LogWriter getLogWriter()
{
var container = HttpContext.Current.Application.GetContainer();
return container.Resolve<LogWriter>();
}
The Unity.Web namespace is needed to allow us to invoke the GetContainer() extension method.