Feb 19
ELMAH with ASP.NET MVC
icon1 Darrell Mozingo | icon2 ASP.NET MVC | icon4 February 19th, 2009| icon37 Comments »

We finally got to the point of needing some error reporting in our new application. We’d read about ELMAH before and assumed we’d use that, but that was a while before we decided to go with the ASP.NET MVC framework instead of the traditional WebForms.

I was a little worried we’d hit some road block using ELMAH in conjunction with ASP.NET MVC, but it actually works out of the box without a hitch. ELMAH has a built in module for displaying any logged errors, and is accessible (by default) via http://localhost/elmah.axd. The latest releases of ASP.NET MVC automatically ignore routes with a .axd extension, though I’m not sure for how many releases they’ve been including that, so earlier releases will have a problem getting to that URL.

Simone Busoli wrote up an excellent article on the most important features of ELMAH and getting it setup. A must read if you’re going to implement this solution.

Here’s a quick run-down of the steps we went through for our application, though (using SQL logging & emailing the developers whenever an exception is logged):

  1. Reference the ELMAH.dll assembly in your project.
  2. Define the section group in your web.config:
    <sectionGroup name="elmah">
        <section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah" />
        <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
        <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
    </sectionGroup>
  3. Define the needed ELMAH section elements, along with their related SQL connection string, in your web.config (we’re only allowing access to the error page via the local host):
    <elmah>
        <security allowRemoteAccess="0" />
        <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="ErrorLogging" />
        <errorMail
            from="PPError@PatriotSoftware.com"
            to="PPDevs@SynergyDataSystems.com"
            subject="Patriot Pay Error"
            async="true"
            smtpPort="25"
            smtpServer="imail.topechelon.corp" />
    </elmah>
     
    <connectionStrings>
        <add name="ErrorLogging" connectionString="Data Source=...." />
    </connectionStrings>
  4. Add the follow handler to the section that’ll allow you to view the errors (I used the path errors.axd instead of the default elmah.axd, just because I hate using default URL like that in case we ever allow this on something other than localhost w/a restricted login):
    <add verb="POST,GET,HEAD" path="errors.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
  5. Add the following modules to the section, which will actually catch exceptions for logging to SQL and emailing out:
    <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
    <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />
  6. (Optional) Add the following methods to your Global.asax, which will filter all exceptions thrown on the local host and ignore them (so developing on your local machine won’t keep emailing everyone else, which, trust me, gets old pretty damn quick):
    public void errorLog_Filtering(object sender, ExceptionFilterEventArgs e)
    {
        if(ErrorFiltering.Filter(new HttpContextWrapper(e.Context)))
        {
            e.Dismiss();
        }
    }
     
    public void errorMail_Filtering(object sender, ExceptionFilterEventArgs e)
    {
        if(ErrorFiltering.Filter(new HttpContextWrapper(e.Context)))
        {
            e.Dismiss();
        }
    }

    and in another file somewhere (note the HttpContextWrapper in both of these pieces of code, for easier testing):

    public class ErrorFiltering
    {
        public static bool Filter(HttpContextWrapper httpContextWrapper)
        {
            return httpContextWrapper.Request.IsLocal;
        }
    }
  7. Run the SQL script included with the ELMAH download to generate the needed table and stored procedures, then hookup SQL security and the connection string from the previous steps

You should be good to go now, ripe with error logging goodness. For added user friendliness, we use the following in our web.config to redirect users to nice pages when an error pops up:

<customErrors mode="RemoteOnly" defaultRedirect="~/Errors/Generic.html">
    <error statusCode="404" redirect="~/Errors/NotFound.html" />
    <error statusCode="500" redirect="~/Errors/Internal.html" />
</customErrors>

The only thing that buggs me about using ELMAH is how Resharper acts with it. The SectionHandler’s defined in the sectionGroup in the web.config (from step 2 above) are internal to the ELMAH assembly, so Resharper freaks out that saying they’re not defined. Bzzt. Sorry, Resharper, try again. So I’ve simply built a local version of the project with those attributes marked as public and it’s all good. I’m looking into filing a bug report with Resharper on this issue now, as it does it with log4net too. Quite annoying when you have the Solution-wide Analysis option turned on and the web.config consistently shows up with “errors”.

Feb 10
Generic NHibernate User Type Base Class
icon1 Darrell Mozingo | icon2 NHibernate | icon4 February 10th, 2009| icon31 Comment »

NHibernate allows you to create custom types for situations where you need more than what’s provided by default (int, string, decimal, etc). For example, we have a custom Money, Percent, and Hour value types in our domain which are, for the most part, immutable generic wrappers around a decimal. We wanted to store these types as decimals in the database, but using the provided decimal type just wouldn’t work.

Reading Jakob Andersen’s nice short article on creating custom NHibernate user types gave us the solution we needed, and after creating user types for those three values types, I quickly began getting bored with the repetitiveness of it all.

I figured we’d have a few more user types for the project, so I decided to refactor out as much as I could from the user types themselves. The result is a generic base class that handles all the mundane details of implementing an NHibernate user type:

public abstract class BaseImmutableUserType<T> : IUserType
{
	public abstract object NullSafeGet(IDataReader rs, string[] names, object owner);
	public abstract void NullSafeSet(IDbCommand cmd, object value, int index);
	public abstract SqlType[] SqlTypes { get; }
 
	public new bool Equals(object x, object y)
	{
		if(ReferenceEquals(x, y))
		{
			return true;
		}
 
		if(x == null || y == null)
		{
			return false;
		}
 
		return x.Equals(y);
	}
 
	public int GetHashCode(object x)
	{
		return x.GetHashCode();
	}
 
	public object DeepCopy(object value)
	{
		return value;
	}
 
	public object Replace(object original, object target, object owner)
	{
		return original;
	}
 
	public object Assemble(object cached, object owner)
	{
		return DeepCopy(cached);
	}
 
	public object Disassemble(object value)
	{
		return DeepCopy(value);
	}
 
	public Type ReturnedType
	{
		get { return typeof(T); }
	}
 
	public bool IsMutable
	{
		get { return false; }
	}
}

Pretty much boiler plate code. This allows us to specify just what we need in each user type’s implementation, such as this one for our Money value type:

public class MoneyUserType : BaseImmutableUserType<Money>
{
	public override object NullSafeGet(IDataReader rs, string[] names, object owner)
	{
		var amount = NHibernateUtil.Decimal.NullSafeGet(rs, names[0]).To<decimal?>();
		return amount.ToMoney();
	}
 
	public override void NullSafeSet(IDbCommand cmd, object value, int index)
	{
		var moneyObject = value as Money;
		object valueToSet;
 
		if (moneyObject != null)
		{
			valueToSet = moneyObject.Amount;
		}
		else
		{
			valueToSet = DBNull.Value;
		}
 
		NHibernateUtil.Decimal.NullSafeSet(cmd, valueToSet, index);
	}
 
	public override SqlType[] SqlTypes
	{
		get
		{
			return new[]
			       	{
			       		SqlTypeFactory.Decimal
			       	};
		}
	}
}

Refactoring out all the unneeded junk, as simple as it might have been, allows other developers to get a quicker grasp of the system and small pieces in it. I know I hate having to break down a large class with lots of unneeded code in it, and this base class allows us to reduce the actual user type down enough to view on a single screen.

This also allowed us to reduce the tests we were doing on each user type to three: one for the get and set methods, and one for the SqlTypes property. All the base class’ functionality can be tested separately, and only once. Less tests (while still maintaining adequate coverage) = easier comprehension = lower maintenance costs.

In a future post I’ll show a test fixture base class we use to easily test each user type.