Using the ASP.NET compiler (via the aspnet_compiler.exe or AspNetCompiler MSBuild task) as part of your build process is a recommended approach to increase application performace and precompile views. If you introduce NHibernate into the picture, you may run into hard to debug exceptions during the precompilation step. For example, when the ISessionFactory is created, it will quote any column or table names that interfere with reserved keywords. If your database doesn’t exist or isn’t accessible from the build server, you’re going to get a connection exception.

It would be better if we didn’t have to query the database during the pre-compilation step (because it doesn’t actually need to!).

Solution 1

The first attempt, at least to our exception above, is to disable the column and table name quoting. There are a couple of ways to acheive this. For example, through XML configuration:

<property name="hbm2ddl.keywords">none</property>

Adversely, doing this puts us at risk of using column or table names that aren’t valid. Disabling this during debugging is OK, but our build server should be as rigid as possible.

Solution 2

The better approach is to not configure the ISessionFactory during pre-compilation. Therefore, we need a way to detect if the application is being pre-compiled.

NOTE: This code uses implementation details and looks into the bowels of ASP.NET that could change in the future. I’ve tested it against .NET 2.0, 3.5 and 4.0 using both ASP.NET compilers. If anyone has a better approach for detecting precompilation, I’d suggest taking a look at my SO post.

public static bool IsPerformingPrecompilation()
{
    var simpleApplicationHost = Assembly.GetAssembly(typeof(HostingEnvironment))
                                    .GetType("System.Web.Hosting.SimpleApplicationHost", 
                                             throwOnError: true, ignoreCase: true);

    return HostingEnvironment.InClientBuildManager &amp;&amp; 
           HostingEnvironment.ApplicationID.EndsWith("_precompile", StringComparison.InvariantCultureIgnoreCase) &amp;&amp; 
           HostingEnvironment.ApplicationHost.GetType() == simpleApplicationHost;
}

Now, we can easily disable creation of the ISessionFactory if the application is being precompiled. You could, for example, put this code inside of your IoC container’s setup:

if (IsPerformingPreCompilation())
{
    return;
}

// Configuration creation snipped...
var sessionFactoy = configuration.BuildSessionFactory();
// Wire up sessionFactory with your IoC container etc.

Conclusion

NHibernate shouldn’t have to hit the database when using the ASP.NET compiler. The simplest fix is to ignore creation of the ISessionFactory all together. Although this approach might be a bit adventurous, I’ve been using it for a couple of applications without any issues.

It’s not uncommon for ASP.NET MVC developers for find things missing out of the box. Now that MVC is open source, the missing puzzle pieces might actually make it into future releases.

Problem

Sending and model binding JSON with an enum is one of the missing pieces. For example, let’s assume we have the following models:

public enum Suit
{
    Spades = 1,
    Hearts = 2,
    Clubs = 3,
    Diamonds = 4
}

public class Card
{
    public Suit Suit { get; set; }
    public int Value { get; set; }
}

And assume that we’ve sent the following JSON back to our CardsController:

// Six of hearts.
{
  'Suit': 2,
  'Value': 6
}

This doesn’t work as expected:

invalid enum model binding

Why is the Suit is 0? Well, it turns out that the default model binder only handles enums by the string representation of their name. Therefore, sending the following JSON works as expected:

{
  'Suit': 'Hearts',
  'Value': 6
}

Sending this type of enum representation back to a controller is counter intuitive (especially if you’re also representing the enum in JavaScript!).

Solution

The following model binder can be used to model bind enum values like { 'Suite': 2 }. You can specify a default enum value if necessary.

public class EnumModelBinder<T> : IModelBinder
        where T : struct
{
    private readonly T defaultValue;
    private readonly bool hasDefaultValue;

    public EnumModelBinder(T defaultValue)
    {
        this.defaultValue = defaultValue;
        this.hasDefaultValue = true;
    }

    public EnumModelBinder()
    {
        this.hasDefaultValue = false;
    }

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        var modelState = new ModelState() { Value = valueResult };

        object actualValue = null;

        if (valueResult == null && this.hasDefaultValue)
        {
            actualValue = this.defaultValue;
        }
        else if (valueResult == null)
        {
            modelState.Errors.Add("No default representation of enum " + typeof(T).Name + " could be found.");
        }
        else
        {
            string value = valueResult.AttemptedValue;
            T enumValue;

            if (Enum.TryParse<T>(value, out enumValue) && Enum.IsDefined(typeof(T), enumValue))
            {
                actualValue = enumValue;
            }
            else if (this.hasDefaultValue)
            {
                actualValue = this.defaultValue;
            }
            else
            {
                modelState.Errors.Add("Could not parse " + value + 
                                      " as a valid numerical value of enum " + typeof(T).Name + ".");
            }
        }

        bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
        return actualValue;
    }
}

Wire this up inside your Global.asax:

ModelBinders.Binders.Add(typeof(Suit), new EnumModelBinder<Suit>());

And now we can go home happy campers:

fixed enum model binding

Conclusion

Because model binding enum values isn’t supported out of the box in ASP.NET MVC, we have to create our own binder. However, I still think this type of binder should be baked into the framework.

Enjoy!


Appendix

Here are the associated tests for this model binder:

[TestClass]
public class EnumModelBinderTest
{
    private enum Foo
    {
        A = 1,
        B = 2,
        C = 3
    }

    [TestMethod]
    public void Bind_to_default_value_if_value_provider_has_no_value()
    {
        // Arrange
        var b = TestableEnumModelBinder<Foo>.Create("X", null, Foo.B);

        // Act
        var result = b.BindModel(b.ControllerContext, b.BindingContext);

        // Assert
        Assert.AreEqual(Foo.B, result);
        Assert.IsTrue(b.BindingContext.ModelState.IsValid);
    }

    [TestMethod]
    public void Bind_to_default_value_if_parsing_fails()
    {
        // Arrange
        var b = TestableEnumModelBinder<Foo>.Create("X", "adsf", Foo.C);

        // Act
        var result = b.BindModel(b.ControllerContext, b.BindingContext);

        // Assert
        Assert.AreEqual(Foo.C, result);
        Assert.IsTrue(b.BindingContext.ModelState.IsValid);
    }

    [TestMethod]
    public void Add_error_if_value_provider_has_no_value_and_no_default_value_is_set()
    {
        // Arrange
        var b = TestableEnumModelBinder<Foo>.Create("X", null);

        // Act
        var result = b.BindModel(b.ControllerContext, b.BindingContext);

        // Assert
        Assert.IsNull(result);
        Assert.IsFalse(b.BindingContext.ModelState.IsValid);
    }

    [TestMethod]
    public void Add_error_if_value_could_not_be_parsed_as_enum_value_and_no_default_value_is_set()
    {
        // Arrange
        var b = TestableEnumModelBinder<Foo>.Create("X", "999");

        // Act
        var result = b.BindModel(b.ControllerContext, b.BindingContext);

        // Assert
        Assert.IsNull(result);
        Assert.IsFalse(b.BindingContext.ModelState.IsValid);
    }

    [TestMethod]
    public void Bind_correct_value()
    {
        // Arrange
        var b = TestableEnumModelBinder<Foo>.Create("X", "2");

        // Act
        var result = b.BindModel(b.ControllerContext, b.BindingContext);

        // Assert
        Assert.AreEqual(Foo.B, result);
        Assert.IsTrue(b.BindingContext.ModelState.IsValid);
    }

    private class TestableEnumModelBinder<T> : EnumModelBinder<T>
        where T : struct
    {
        public ControllerContext ControllerContext;
        public ModelBindingContext BindingContext;

        private TestableEnumModelBinder(T defaultValue)
            : base(defaultValue)
        {

        }

        private TestableEnumModelBinder()
            : base()
        {

        }

        public static TestableEnumModelBinder<T> Create(string modelName, string modelValue, object defaultValue = null)
        {
            var controllerContext = new ControllerContext();
            var valueProvider = new Mock<IValueProvider>();

            if (modelValue != null)
            {
                valueProvider.Setup(p => p.GetValue(modelName))
                    .Returns(new ValueProviderResult(modelValue, modelValue, CultureInfo.CurrentCulture));
            }
            else
            {
                valueProvider.Setup(p => p.GetValue(modelName)).Returns((ValueProviderResult)null);
            }

            var bindingContext = new ModelBindingContext()
            {
                ModelName = modelName,
                ValueProvider = valueProvider.Object
            };

            TestableEnumModelBinder<T> binder;

            if (defaultValue != null)
            {
                binder = new TestableEnumModelBinder<T>((T)defaultValue);
            }
            else
            {
                binder = new TestableEnumModelBinder<T>();
            }

            binder.ControllerContext = controllerContext;
            binder.BindingContext = bindingContext;

            return binder;
        }
    }
}

I’ve often found myself writing raw SQL queries with NHibernate because the abstraction can be limiting. This can be achieved with the CreateSQLQuery() method on ISession:

var results = this.session.CreateSQLQuery("select Id, Title, Body from [Posts]")
    .AddEntity(typeof(Post))
    .List<Post>();

For a simple case like this, everything works as expected. However, if the query includes something that breaks the NHibernate abstraction, like in this StackOverflow post, it’s not really possible to AddEntity(). When abstractions get in the way, it causes developer frustration and many wasted hours trying to fight the uphill battle. Sometimes I want NHibernate to get out of my way and just give me some data - I don’t want to have to add extra entity mappings!

Being Dynamic

Recently, I’ve been playing around with Dapper and Massive. I really like the ability to mingle directly with the data using dynamics. Wouldn’t it be cool if we could do the same thing using NHibernate?

Why not!

public static class NhTransformers
{
    public static readonly IResultTransformer ExpandoObject;

    static NhTransformers()
    {
        ExpandoObject = new ExpandoObjectResultSetTransformer();
    }

    private class ExpandoObjectResultSetTransformer : IResultTransformer
    {
        public IList TransformList(IList collection)
        {
            return collection;
        }

        public object TransformTuple(object[] tuple, string[] aliases)
        {
            var expando = new ExpandoObject();
            var dictionary = (IDictionary<string, object>)expando;
            for (int i = 0; i < tuple.Length; i++)
            {
                string alias = aliases[i];
                if (alias != null)
                {
                    dictionary[alias] = tuple[i];
                }
            }
            return expando;
        }
    }
}

public static class NHibernateExtensions
{
    public static IList<dynamic> DynamicList(this IQuery query)
    {
        return query.SetResultTransformer(NhTransformers.ExpandoObject)
                    .List<dynamic>();
    }
}

How awesome is this?

var results = this.session.CreateSQLQuery("select Id, Title, Body from [Posts]")
                  .DynamicList(); // Secret sauce!
// results are now dynamic!
Console.WriteLine(results[0].Id);
Console.WriteLine(results[0].Name);
// rock on!

Conclusion

NHibernate is quite the tool - but it can be too abstracted. Using DynamicList() gives data control back to the developer when raw SQL access is required.