Thursday, 11 March 2010

No more NHibernate LazyInitializationExceptions

For the last few months I have been working on a web application using S#arp Architecture with NHibernate.  Now S#arp Architecture give us a nice session-per-request model for the NHibernate session but we needed to keep state over web requests and preferred to do it without changing to the long running conversation model.  Therefore we stored our application service in the ASP .NET Cache to be picked up by the next web request for that session which could then carry on using the entities therein.  Great, until we tried to lazy load a property when we would get the NHibernate.LazyInitializationException because, of course, the session the entities were originally loaded into no longer existed.  A quick search on the web told us that our detached entities needed re-attaching to the session using either the Update or Lock methods depending on whether we knew they had changed or not - we had no way of knowing whether changes had been made so used the Update method.  This was not ideal as everything would then get written to the database when a transaction was committed - not what we needed when the majority of it was lookup data.


So, what to do?  Some investigation into the NHibernate source and I found that lazy initialization (of non-collections anyway) was performed by an implementation of the BasicLazyInitializer abstract class, the solution seemed quite simple create a subclass of our current LazyIntializer (in this case NHibernate.ByteCode.Castle.LazyInitializer) override the method that does the initialization to check if a session is available and if it's not get one before carrying on the call the base method.  It turned out this could be done in the Intercept method and the code for our lazy initializer looks like this:


namespaceProxy
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using Castle.Core.Interceptor;
    using NHibernate.ByteCode.Castle;
    using NHibernate.Engine;
    using NHibernate.Type;

    [Serializable]
    public class LazyInitializer : LazyInitializer, IInterceptor
    {
        public LazyInitializer(string entityName, Type persistentClass, object id, MethodInfo getIdentifierMethod, MethodInfo setIdentifierMethod, IAbstractComponentType componentIdType, ISessionImplementor session)
            : base(entityName, persistentClass, id, getIdentifierMethod, setIdentifierMethod, componentIdType, session)
        {
        }

        public override void Intercept(Castle.Core.Interceptor.IInvocation invocation)
        {
            // If we don't have a session at this point then we will get a lazy initialization
            // error if we carry on.  So before we do we can set the session to our current default
            if (this.Session == null)
            {
                this.Session = Helper.SessionHelper.DefaultSession.GetSessionImplementation();
            }

            base.Intercept(invocation);
        }
    }
}




Then all that was required was to create a new ProxyFactory to return the new LazyInitializer as part of it's GetProxy method like so:
namespace Proxy
{
    using System;
    using Castle.DynamicProxy;
    using NHibernate;
    using NHibernate.ByteCode.Castle;
    using NHibernate.Proxy;

    public class NewProxyFactory : ProxyFactory
    {
        private static readonly ProxyGenerator proxyGenerator = new ProxyGenerator();

        public override INHibernateProxy GetProxy(object id, NHibernate.Engine.ISessionImplementor session)
        {
            INHibernateProxy proxy;
            try
            {
               LazyInitializer initializer = new LazyInitializer(this.EntityName, this.PersistentClass, id, this.GetIdentifierMethod, this.SetIdentifierMethod, this.ComponentIdType, session);
                object obj2 = this.IsClassProxy ? proxyGenerator.CreateClassProxy(this.PersistentClass, this.Interfaces, new Castle.Core.Interceptor.IInterceptor[] { initializer }) : proxyGenerator.CreateInterfaceProxyWithoutTarget(this.Interfaces[0], this.Interfaces, new Castle.Core.Interceptor.IInterceptor[] { initializer });
                initializer._constructed = true;
                proxy = (INHibernateProxy)obj2;
            }
            catch (Exception exception)
            {
                log.Error("Creating a proxy instance failed", exception);
                throw new HibernateException("Creating a proxy instance failed", exception);
            }

            return proxy;
        }
    }
}
And a new ProxyFactoryFactory which is the class referenced in the NHibernate config file:
namespace Proxy
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using NHibernate.Bytecode;
    using NHibernate.Proxy;

    public class ProxyFactoryFactory : IProxyFactoryFactory
    {
        public NHibernate.Proxy.IProxyValidator ProxyValidator
        {
            get 
            {
                return new DynProxyTypeValidator();
            }
        }

        public NHibernate.Proxy.IProxyFactory BuildProxyFactory()
        {
            return new NewProxyFactory();
        }
    }
}
Unfortunately the above doesn't deal with collections so a separate set of classes was required to correctly lazy initialize a collection.  Working on the same technique as above I created a subclass of the PersistentGenericBag class with overrides for members that will require a session - in our case this turned out to be the Count property and the GetEnumerator method.  In addition to checking if a session was available it was also necessary to check if the collection had previously been initialized, using the WasInitialized property, so that we didn't accidentally add a session to a collection that shouldn't have one yet.  The class ended up like the following code:
namespace Proxy
{
    using System;
    using System.Collections.Generic;
    using NHibernate;
    using NHibernate.Collection.Generic;
    using NHibernate.Engine;
    using NHibernate.Persister.Collection;

    [Serializable]
    public class NewPersistentGenericBag<T> : PersistentGenericBag<T>, IList<T>
    {
        public NewPersistentGenericBag() 
            : base()
        { 
        }

        public NewPersistentGenericBag(ISessionImplementor session) 
            : base(session) 
        {
        }

        public NewPersistentGenericBag(ISessionImplementor session, ICollection<T> coll)
            : base(session, coll)
        {
        }

        int ICollection<T>.Count
        {
            get
            {
                this.CheckSession();

                return this.Count;
            }
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            this.CheckSession();

            return this.GetEnumerator() as IEnumerator<T>;
        }

        private void CheckSession()
        {
            if (this.Session == null && !this.WasInitialized)
            {
this.SetCurrentSession(Helper.SessionHelper.DefaultSession.GetSessionImplementation());
                ICollectionPersister persister = this.Session.Factory.GetCollectionPersister(this.Role);
                this.Session.PersistenceContext.AddUninitializedCollection(persister, this, this.Owner.Id);
                EntityKey ownerKey = new EntityKey(this.Owner.Id, persister.OwnerEntityPersister, this.Session.EntityMode);
                if (!this.Session.PersistenceContext.ContainsEntity(ownerKey))
                {
                    this.Session.PersistenceContext.AddEntity(this.Owner, Status.Loaded, null, ownerKey, persister.OwnerEntityPersister.GetVersion(this.Owner, this.Session.EntityMode), LockMode.None, true, persister.OwnerEntityPersister, false, true);
                }
            }
        }
    }
}
As well as the new collection type a CollectionType implementation was required to return the new persistent bag type:
namespace Proxy
{
    using System;
    using System.Collections.Generic;
    using NHibernate.Collection;
    using NHibernate.Engine;
    using NHibernate.Persister.Collection;
    using NHibernate.Type;

    public class PersistentGenericBagType<T> : BagType
    {
        public NewPersistentGenericBagType(string role, string propertyRef)
            : base(role, propertyRef, false)
        {
        }

        public override Type ReturnedClass
        {
            get 
            { 
                return typeof(IList<T>); 
            }
        }

        public override IPersistentCollection Wrap(ISessionImplementor session, object collection)
        {
            return new NewPersistentGenericBag<T>(session, collection as IList<T>);
        }

        public override object Instantiate(int anticipatedSize)
        {
            return anticipatedSize <= 0 ? new List<T>() : new List<T>(anticipatedSize + 1);
        }

        public override IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister, object key)
        {
            return new NewPersistentGenericBag<T>(session);
        }
    }
}
And finally an implementation of ICollectionTypeFactory to return the new collection type when asked for a collection - this factory was then added to the NHibernate config file so that the new collection became the default collection type.  The simple code for this:
namespace Proxy
{
    using NHibernate.Type;

    public class CollectionTypeFactory : DefaultCollectionTypeFactory
    {
        public override CollectionType Bag<T>(string role, string propertyRef, bool embedded)
        {
            return new PersistentGenericBagType<T>(role, propertyRef);
        }
    }
}
All that was left was to add/change the entries in the config file for the proxyfactory.factory_class and collectiontype.factory_class properties to point to the new factory classes and we have a data layer in which we can use lazy loading without having to re-attach objects for each web request which increases performance and reduces code complexity.

Saturday, 30 January 2010

NHibernate Generated SQL

The other day I needed to find the SQL generated by NHibernate to be displayed at runtime - the 'displayed at runtime' part being the trickiest, obviously I could set the show_sql property to log the sql but that wasn't what was required.

A search around the internet yielded nothing so I looked into the NHibernate assembly and found the NHibernate.Engine.Query.HqlPlan class which contained a property, SqlStrings, which suggested it would do exactly as I needed. And so it turned out, I instantiated the class like so:

new NHibernate.Engine.Query.HqlPlan(hqlQueryString, false, new System.Collections.Generic.Dictionary(), factoryImplementor);

and sure enough the SqlStrings property contained the SQL to be executed for 'hqlQueryString'.

Friday, 17 October 2008

Silverlight DataGrid as Properties Window

Today I needed to use the Silverlight DataGrid control as a Visual Studio-like properties window - basically showing a list of Key/Value pairs and allowing editing of the value via a suitable control. At the moment our properties are only of type string or bool so the control required was either a TextBox or a CheckBox depending on the type of the value.

Firstly I set the AutoGenerateColumns property on the grid to True in the hope that it might be clever enough to realise what I was trying to do and render the grid appropriately. Not surprisingly the value column was built as a DataGridTextColumn and all the bool values shown as their ToString().

Eventually I came up with using a DataGridTemplateColumn containing each type of control we needed in the DataTemplate, binding the Visibility property to the value and using a converter to decide whether or not the control should be visible depending on the type of the value.

The XAML for the grid ended up like this:


<controls:DataGrid x:Name="properties" Grid.Row="3" AutoGenerateColumns="False" >
<controls:DataGrid.Columns>
<controls:DataGridTextColumn Header="Name" IsReadOnly="True" Binding="{Binding Name, Mode=OneTime}"/>
<controls:DataGridTemplateColumn Header="Value">
<controls:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding Value, Mode=TwoWay}" Background="Transparent" BorderBrush="Transparent" Visibility="{Binding Value, Converter={StaticResource ValueVisibilityConverter}, ConverterParameter='String'}"/>
<CheckBox IsChecked="{Binding Value, Mode=TwoWay}" Background="Transparent" Visibility="{Binding Value, Converter={StaticResource ValueVisibilityConverter}, ConverterParameter='Boolean'}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</controls:DataGridTemplateColumn.CellTemplate>
</controls:DataGridTemplateColumn>
</controls:DataGrid.Columns>
</controls:DataGrid>


It uses the following IValueConverter implementation to convert the value into a Visibility value - the parameter is the data type that the control will show ie. String, Boolean so we should be able to easily add more controls as our types of property increase.



public class ValueVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Visibility result = Visibility.Collapsed;
if(value != null)
{
if (value.GetType().Name == parameter.ToString())
{
result = Visibility.Visible;
}
}

return result;
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}


Friday, 10 October 2008

LINQ to Entities LIKE Operator

A couple of weeks ago I was asked to move some search functionality that had been prototyped in LINQ to SQL into production code for which we are using the Entity Framework and LINQ to Entities.

One of the main requirements of the search was that we have the ability to query any text column in the search table using any combination of wildcard the user liked. The LINQ to SQL prototype used the SqlMethods.Like method but as this is only available in the SQL implementation of LINQ another method was required. A search around the internet basically said that there is no LIKE operator in LINQ to Entities but the StartsWith(), EndsWith() and Contains() methods on the string class can give the functionality of '%smith', '%smith%' and 'smith%' but I couldn't find a solution that would allow us to use wildcards as per the requirements.

I eventually decided that I'd try and write an extension method on IQueryable that would provide this functionality - while looking into how to go about this I found a Where() method on System.Data.Objects.ObjectQuery which takes a parameter of string and a list of parameters. It turned out that this allowed us to provide a predicate using Entity SQL and, as ObjectQuery implements IQueryable the result can be used as part of a LINQ to Entities query as well.

So our search query could look something like this:

EntityContainer.Customers.Where("It.Surname like '%sm%i%th%' and It.Forename like 'j%o%h'");

Obviously this doesn't give us the type safety of a LINQ implementation in the predicate but is certainly useful for search functionality.

Saturday, 4 October 2008

The First Post

Hello!

So what do you write in your first post? The obvious answer being whatever you like because no-one will be reading it!

Basically, recently I've been doing a lot of work using Silverlight, WCF, LINQ and the Entity Framework and while there are a lot of resources around there are still things that I've needed to do that, when searching online, I have found very little about. Therefore I figured when I solve an issue that has little or no mention anywhere why not blog about it myself and maybe help others that have the same problem. For instance I'll post about a LIKE operator in LINQ to Entities soon which was a problem with a simple solution that I only found more complicated solutions to.

Shortly I'll also be starting to develop a cricket scoring application that will, hopefully, be used to score league cricket matches here in the UK as I think this needs to be made easier to do so that more people are able to score the game. When I get going with this project I'll also share some of my thoughts on how I will be putting that together.