NHibernate 3 - Extending the Linq Provider to fix some System.NotSupportedException

With the release of the new version NHibernate (3.0 alpha1), I’ve decided to give it a try and branch my current solution to switch to this new version and see hoe it goes.

I was especially interested in the new Linq support, cause I’ve decided to use it as the basis for my data access strategies.

After the necessary reference changes I run all my test suit...and I had bad news from the Linq provider in the form of some System.NotSupportedException like this one:

“UnitTest.Test03_Linq.QueryWithEqualsGuid:
System.NotSupportedException : Boolean Equals(System.Guid)”

Being NHibernate an open source project, instead of bothering the guys responsible for the project, my first approach is always to look at the code; so I downloaded the trunk and started looking around at the Linq provider. Watching how Equals() methods are handled by the parser it comes out fast that only the specific version to deal with strings is currently supported [bool Equals(string)], all other types have to rely on the ==operator.

But in my code I had a lot of filters based on Equals() call for various object types (int, guid and so on...) and I didn’t wanted to touch that code especially considering that with the previous Linq provider everything was working well.

However the solution is easy, just extend the default EqualsGenerator adding the support for the missing methods; but I didn’t wanted to compile a specific ‘patched’ version of NHibernate and this post from Fabio Maulo confirmed me you can easily extend the Linq provider. Great! That’s was exactly what I was looking for!

I started working on it and I had my second surprise Open-mouthed smile. The Linq provider was subhect of a heavy refactoring activity to provide better extensibility from the version you have in 3.0 alpha1. Using reflector and looking around in the binaries it comes out that to extend that provider you have to register your extension methods calling the methods of the NHibernate.Linq.Functions.FunctionRegistry class. But in all honesty I think that the way it works in alpha2 is way more elegant and it follows better the standard approach NHibernate have when it comes to configure its components.

So if you have to extend the Linq provider forget of alpha1 and compile your own version of NHibernate getting it from the Trunk.

Back to the job now: following Fabio’s instructions (and looking at the code) I came out with these classes:

public class ExtendedEqualsGenerator : BaseHqlGeneratorForMethod
{
    public ExtendedEqualsGenerator()
    {
        // the methods call are used only to get info about the signature, the actual parameter is just ignored
        SupportedMethods = new[] { 
            ReflectionHelper.GetMethodDefinition<Byte>(x => x.Equals((Byte)0)),
            ReflectionHelper.GetMethodDefinition<SByte>(x => x.Equals((SByte)0)),
            ReflectionHelper.GetMethodDefinition<Int16>(x => x.Equals((Int16)0)),
            ReflectionHelper.GetMethodDefinition<Int32>(x => x.Equals((Int32)0)),
            ReflectionHelper.GetMethodDefinition<Int64>(x => x.Equals((Int64)0)),
            ReflectionHelper.GetMethodDefinition<UInt16>(x => x.Equals((UInt16)0)),
            ReflectionHelper.GetMethodDefinition<UInt32>(x => x.Equals((UInt32)0)),
            ReflectionHelper.GetMethodDefinition<UInt64>(x => x.Equals((UInt64)0)),
            ReflectionHelper.GetMethodDefinition<Single>(x => x.Equals((Single)0)),
            ReflectionHelper.GetMethodDefinition<Double>(x => x.Equals((Double)0)),
            ReflectionHelper.GetMethodDefinition<Boolean>(x => x.Equals(true)),
            ReflectionHelper.GetMethodDefinition<Char>(x => x.Equals((Char)0)),
            ReflectionHelper.GetMethodDefinition<Decimal>(x => x.Equals((Decimal)0)),
            ReflectionHelper.GetMethodDefinition<Guid>(x => x.Equals(Guid.Empty)),
        };
    }
  
    public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
    {
        return treeBuilder.Equality(
                visitor.Visit(targetObject).AsExpression(),
                visitor.Visit(arguments[0]).AsExpression());
    }
}
  
public class ExtendedLinqtoHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
    public ExtendedLinqtoHqlGeneratorsRegistry()
    {
        this.Merge(new ExtendedEqualsGenerator());
    }
}

After registering them (actually it can be done only in code) using:

configuration.SetProperties("linqtohql.generatorsregistry", "Nhibernate.Extensions.ExtendedLinqtoHqlGeneratorsRegistry, Nhibernate.Extensions");
  
or
  
configuration.LinqToHqlGeneratorsRegistry<ExtendedLinqtoHqlGeneratorsRegistry>();

My tests passed again and I didn’t had to touch a single line of code. This is simply amazing! (but I think the full support for all the equals methods should have been added to the core anyway).

你可能感兴趣的:(Hibernate)