Using Reflection to Dynamically Invoke the NHibernate Profiler

November 03, 2010 | NHibernate

If you are using NHibernate as your Object-relational mapping (ORM) solution, then chances are you are also using the NHibernate Profiler. You can see the exact SQL queries sent to the database and also detect common pitfalls, among others.

NHibernate Profiler is developed by Hibernating Rhinos and Blue Spire Consulting, Inc. The main contributers behind this tool are Oren Eini (NHibernate, Castle Project, RavenDB), Christopher Bennage (Sams Teach Yourself WPF in 24 Hours) and Rob Eisenberg (Sams Teach Yourself WPF in 24 Hours, Caliburn, Caliburn Micro). As you can see, this tool is not only interesting for what it does, but it has also interesting people behind it.

Using the NHibernate Profiler in your application is very easy:

  1. Add a reference to HibernatingRhinos.Profiler.Appender.dll assembly.
  2. Invoke the NHibernateProfiler.Initialize method from your code.

The problem is, you (might) need to remove the referenced assembly every time you ship. Even if you decide to keep it, you need to invoke the Initialize method from your code inside a method marked with the ConditionalAttribute class so the compiler ignores it unless you are running in debug mode.

You can avoid adding a reference (step 1), and invoke the Initialize method dynamically using reflection:

1. In the <appSettings> element include the following keys:

<appSettings>
    <add key="NHibernate_ProfilerAssembly"
         value="HibernatingRhinos.Profiler.Appender.dll"/>
    <add key="NHibernate_ProfilerDirectory"
         value="<your_path>"/>
    <add key="NHibernate_ProfilerMethod"
         value="Initialize"/>
    <add key="NHibernate_ProfilerType"
         value="NHibernateProfiler"/>
</appSettings>

2. For housekeeping you can define an interface.

public interface IIntegrateProfiler
{
    void SetNHibernateProfiler(string directory, string assembly);

    bool CanUseProfiler { get; }

    string ProfilerPath { get; }
}

3. Inside the your bootstrapper class, or the class where the ISessionFactory is built, implement the above interface.

In the example below the ValidateDirectory and ValidateFileName methods are not included as well as the this.profilerPath  private instance field.

public void SetNHibernateProfiler(string directory, string assembly)
{
    if (ValidateDirectory(directory))
    {
        string profilerPath = Path.Combine(directory, assembly);

        if (ValidateFileName(profilerPath))
        {
            this.profilerPath = profilerPath;
        }
        else
        {
            this.profilerPath = string.Empty;
        }
    }
}

public bool CanUseProfiler
{
    get { return !string.IsNullOrWhiteSpace(this.profilerPath); }
}

public string ProfilerPath
{
    get { return this.profilerPath; }
}

4. Finally, from your code you can check whether the CanUseProfiler property returns true and call the InitializeProfiler method below:

private void InitializeProfiler()
{
    Assembly asm  = Assembly.LoadFileLoadFrom(this.options.ProfilerPath);
    Type profiler = asm.GetExportedTypes().Where(x => 
        x.Name == ConfigurationManager.AppSettings["NHibernate_ProfilerType"])
            .FirstOrDefault();

     if (profiler != null)
     {
         MethodInfo initialize = profiler.GetMethods().Where(x => 
           x.Name == ConfigurationManager
             .AppSettings["NHibernate_ProfilerMethod"]).FirstOrDefault();
         if (initialize != null)
         {
             initialize.Invoke(null, null);
         }
     }
}

Actually, the same technique can be used for the other profilers too (EFProfL2SProf, etc).

Don't forget to remove the referenced assembly from your project.