I often use NHibernate to work with SQL database. It’s a very mature ORM solution for .NET which allows You to freeley “talk” with any database. Although, this is very simple ORM, sometimes I have some challanges I have to face.

One of the first issue I had with this ORM was:

Could not load file or assembly ‘NHibernate.ByteCode.Castle’ or one of its dependencies. The system cannot find the file specified.

The message was clear. Somehow my solution couldn’t find  NHibernate.ByteCode.Castle.dll. First I checked the references in my project – they were OK. Than I checked if the dll is in the GAC – It was. Running out of ideas I started to read the error message carefoully and I focused on one part:

According to this message I should turn on the Assembly binding logging to see what is wrong. I went to HKLM\Software\Microsoft\Fusion and created new DWORD value set to 1. After refreshing the error page I noticed a new thing:

After that everything was clear. Application have been searching for the dll in the wrong directory – bin instead of GAC. To fix this I could modify the web.config, but I really don’t like to change contents of this file, unless I really have to. I figured out that I can modify my code, so it will be able to resolve NHibernate.ByteCode.Castle.dll. Before modifications the code looked like this:


public static class NHibernateHelper
 {

public static ISessionFactory GetSessionFactory(bool throwExceptionOnFail)
 {
 var cfg = Fluently.Configure().Database(MsSqlConfiguration.MsSql2008.UseOuterJoin().ConnectionString(x => x.Server("SERVER").Username("USERNAME").Password("PASSWORD").Database("DATABASE"))).Mappings(x => x.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()));

return cfg.BuildSessionFactory();

}

}

After modifications:


public static class NHibernateHelper
 {
 public static ISessionFactory GetSessionFactory()
 {
 return GetSessionFactory(false);
 }

private static ISessionFactory GetSessionFactory(bool throwExceptionOnFail)
 {
 try
 {
 var cfg = Fluently.Configure().Database(MsSqlConfiguration.MsSql2008.UseOuterJoin().ConnectionString(x => x.Server("SERVER").Username("USER").Password("PASSWORD").Database("DATABASE"))).Mappings(x => x.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()));

return cfg.BuildSessionFactory();//This will throw an error first time after application pool recycle/reset.
 }
 catch(FileNotFoundException)
 {
 if (throwExceptionOnFail)
 throw;

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);//Add the resolve event handler

var sessionFactory = GetSessionFactory(true);

AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(CurrentDomain_AssemblyResolve);//Remove the event, because the assembly has been already loaded to the current application domain and will stay there until next application pool recycle/reset

return sessionFactory;
 }
 }

static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
 {
 if (args.Name.ToLower() == "NHibernate.ByteCode.Castle".ToLower())
 return Assembly.Load("NHibernate.ByteCode.Castle, version=3.1.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4");

 return null;
 }
 }

Notice the “AppDomain.CurrentDomain.AssemblyResolve” part. I added the event handler to the current application domain which will fire when any of the DLL’s can’t be resolved. Inside the event handler I check if the missing DLL is NHibernate.ByteCode.Castle. If so, then I load it “manually” and return the Assembly object which will exist in the current application domain until application pool is beeing recycled or reset. The “catch” code will execute only the first time after new application domain is created(after application pool recycle/reset). Every next time only the “try” code will be executed.

Now, to get the ISessionFactory object without any errors only one method should be called:


var sf = NHibernateHelper.GetSessionFactory()

Of course this is not a perfect solution. For example if You replace NHibernate.ByteCode.Castle.dll with a newer version, above code won’t work. Then You will have to make a redirect to this new version.