Logging Application Block in ASP.NET MVC Project

Topics: Logging Application Block
May 25, 2010 at 4:12 PM

I'm looking for a good example of how I would implement the LAB in my ASP.NET MVC project using dependency injection. I've done the service locator method but I'd really like to have my LogWriter instead similar to what I'm doing with my repositories.

I've defined my LAB configuration using the VS integration for configuring the web.config file. Now I need to set this up so that Unity knows about it and that's where I'm having some mental disconnects I'm afraid. I'm missing something simple somewhere probably.

For context, here's what I'm doing with Unity and my repositories. How similar is this to what I need to do to get the LAB integrated in a similar manner? How do I get the information that is defined in the Unity section of my web.config to be read, understood, and injected into my controller's constructor?

I'm currently using Unity 2.0 to inject my repository dependencies into my controllers via constructor injection like below.

  private IProductRepository productRepository;

  public ProductController(IProductRepository productRepository)
  {
    this.productRepository = productRepository;
  }

My web.config contains my Unity configuration. 

<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <sectionExtension type="Company.Common.Unity.ConnectionStringsParameterValueExtension, Ornl.Common.Unity" />

    <alias alias="IProductRepository" type="Ornl.Eshq.BroadPointe.Model.Repositories.IProductRepository, Ornl.Eshq.BroadPointe.Model" />
    <container>
      <!-- IProductRepository -->
      <register type="IProductRepository" mapTo="SqlProductRepository">
        <lifetime type="transient" />
        <constructor>
          <param name="connectionString" >
            <connectionString name="DatabaseName" />
          </param>
        </constructor>
      </register>
    </container>
  </unity>

Here is the Application_Start() event in my Global.asax file.

  protected void Application_Start()
    {
      // Instantiate a dependency injection container.
      IUnityContainer container = new UnityContainer();
      
      // Configure container from web.config.
      UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
      section.Configure(container);
      
      // Set UnitControllerFactory to be the controller factory.
      UnityControllerFactory factory = new UnityControllerFactory(container);
      ControllerBuilder.Current.SetControllerFactory(factory);

      // Register areas.
      AreaRegistration.RegisterAllAreas();

      // Register routes.
      RegisterRoutes(RouteTable.Routes);
    }

Here is the UnityControllerFactory class definition.

using System;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.Practices.Unity;

namespace Ornl.Eshq.BroadPointe.WebUI
{
  public class UnityControllerFactory : DefaultControllerFactory
  {
    private readonly IUnityContainer _container;

    public UnityControllerFactory(IUnityContainer container)
    {
      _container = container;
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
      if (controllerType != null)
        return _container.Resolve(controllerType) as IController;
      else
        return base.GetControllerInstance(requestContext, controllerType);
    }
  }
}

The Logging section of my config looks like this currently.

<loggingConfiguration name="" tracingEnabled="true" defaultCategory="General">
    <listeners>
      <add name="Flat File Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        fileName="D:\Temp\LoggingMVC.log" formatter="Text Formatter" />
    </listeners>
    <formatters>
      <add type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        template="Timestamp: {timestamp}{newline}&#xA;Message: {message}{newline}&#xA;Category: {category}{newline}&#xA;Priority: {priority}{newline}&#xA;EventId: {eventid}{newline}&#xA;Severity: {severity}{newline}&#xA;Title:{title}{newline}&#xA;Machine: {localMachine}{newline}&#xA;App Domain: {localAppDomain}{newline}&#xA;ProcessId: {localProcessId}{newline}&#xA;Process Name: {localProcessName}{newline}&#xA;Thread Name: {threadName}{newline}&#xA;Win32 ThreadId:{win32ThreadId}{newline}&#xA;Extended Properties: {dictionary({key} - {value}{newline})}"
        name="Text Formatter" />
    </formatters>
    <categorySources>
      <add switchValue="All" name="General">
        <listeners>
          <add name="Flat File Trace Listener" />
        </listeners>
      </add>
    </categorySources>
    <specialSources>
      <allEvents switchValue="All" name="All Events">
        <listeners>
          <add name="Flat File Trace Listener" />
        </listeners>
      </allEvents>
      <notProcessed switchValue="All" name="Unprocessed Category">
        <listeners>
          <add name="Flat File Trace Listener" />
        </listeners>
      </notProcessed>
      <errors switchValue="All" name="Logging Errors &amp; Warnings">
        <listeners>
          <add name="Flat File Trace Listener" />
        </listeners>
      </errors>
    </specialSources>
  </loggingConfiguration>
  <system.web>

Thanks for taking the time to look at my question!

Nevada

May 25, 2010 at 4:27 PM
Edited May 25, 2010 at 4:31 PM

The only thing you need to do differently is to add the EnterpriseLibraryCoreExtension to your container, and then add a dependency on a LogWriter to your class. And, well, that's pretty much it.

Something like this in your Unity config should do it:

 

<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <sectionExtension type="Company.Common.Unity.ConnectionStringsParameterValueExtension, Ornl.Common.Unity" />

    <alias alias="IProductRepository" type="Ornl.Eshq.BroadPointe.Model.Repositories.IProductRepository, Ornl.Eshq.BroadPointe.Model" />

  <!-- Make Entlib types more easily accessable -->
    <namespace name="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity" />
    <namespace name="Microsoft.Practices.EnterpriseLibrary.Logging" />
    <assembly name="Microsoft.Practices.EnterpriseLibrary.Common" />
    <assembly name="Microsoft.Practices.EnterpriseLibrary.Logging" />

    <container>
      <!-- Bring in Entlib registrations -->
      <extension type="EnterpriseLibraryCoreExtension" />

      <!-- IProductRepository -->
      <register type="IProductRepository" mapTo="SqlProductRepository">
        <lifetime type="transient" />
        <constructor>
          <param name="connectionString" >
            <connectionString name="DatabaseName" />
          </param>
          <!-- Add this parameter to your constructor -->
          <param name="logWriter" />
        </constructor>
      </register>
    </container>
  </unity>

 

From here, just add your Logwriter as a constructor parameter and the rest should just work.

-Chris

P.S. If you're using Entlib from the GAC you'll need to use the full assembly name with public key token, not the short one like I've done here.

May 25, 2010 at 5:04 PM

Chris,

Thanks for the quick reply. I have it working now.

I actually wanted the LogWriter to be injected into my Controller not my repository so I didn't need the following.

<!-- Add this parameter to your constructor -->
<param name="logWriter" />

However, I want to make sure I'm clear on why this is working. Am I correct in thinking that the following does all the type registrations?

<!-- Bring in Entlib registrations -->
<extension type="EnterpriseLibraryCoreExtension" />

The line above is why my Controller's constructor can ask for a LogWriter type and get the default LogWriter provided by EntLib (which it read from loggingConfiguration section). Am I correct here?

On a side note, if I do want to perform some logging in my repository as well and I set up the parameter as you demonstrate, will Unity be instantiating separate LogWriters for both the controller and the repository?

Thanks for the great feedback on these forums. Looking through the archives it looks like most everyone gets a prompt, polite, and intelligent answer to their questions.

Thanks,

Nevada

May 25, 2010 at 9:28 PM

Yes, loading the EnterpriseLibraryCoreExtension causes all the Entlib type registrations to get loaded into the container. Once you do this, all the Entlib public objects are available for injection into whatever object you want.

Sorry about the confusion about the parameter, I guess I didn't read the config file closely enough, but you've got the idea.

As far as your question about the lifetime, in LogWriter's case, it's registered as a singleton, so you'll get the same instance every time. But the nice thing about getting them out of the container is you don't actually care. The container manages the lifetime; if we were to change LogWriter in the future to be transient, for example, your code would continue to work with no issues.

I can't take credit for the forum feedback, I just pop in occasionally. The support staff from Avenade have been doing a fantastic job of helping folks here!

-Chris