Tag Archives: Fluent NHibernate

NHibernate.Criterion Extensions workaround

I’ve been working with Fluent NHibernate for the past month or so, and I realized while writing an NHibernate query that the NHibernate Criterion’s Restrictions class isn’t very refactor-friendly. The refactorability of Fluent NHibernate is ultimately why we’ve decided to use it. It does take a little longer to set up than some other ORMs, but in the long-run it’s nice to be able to change a property name and refactor.

… except the property names are hard-coded in the Criteria!

My workaround for this is to use a bit of static reflection a-la-FNH, and maintain code that is easily refactored.

For instance, I’ve created a static utility class (Showing the Restrictions.Eq() substitute):

  public static class CriterionExtensions
    {
        public static SimpleExpression Eq<T>(
                Expression<Func<T, object>> exp, object value
        )
        {
            var memberExpression = GetMemberExpression(exp);
            string propertyName = ((PropertyInfo)memberExpression.Member).Name;
            if (!string.IsNullOrEmpty(propertyName))
                return Restrictions.Eq(propertyName, value);
            else
                return null;
        }

        private static MemberExpression GetMemberExpression<T>(
                Expression<Func<T, object>> expression
        )
        {
            MemberExpression memberExpression = null;
            if (expression.Body.NodeType == ExpressionType.Convert)
            {
                var body = (UnaryExpression)expression.Body;
                memberExpression = body.Operand as MemberExpression;
            }
            else if (expression.Body.NodeType == ExpressionType.MemberAccess)
            {
                memberExpression = expression.Body as MemberExpression;
            }
            if (memberExpression == null)
            {
                throw new ArgumentException("Not a member access", "member");
            }
            return memberExpression;
        }
    }

And to use this code, you can create an aliased using directive and call it in code:

    using Ensure = MyNamespace.Extensions.Criterion.CriterionExtensions;
    /* class declarations and whatnot */
     internal IEnumerable<Product> GetAll(int shelfNumber)
      {
          var session = SessionManager.GetCurrentSession();
          return session.CreateCriteria<Product>()
              .Add(Ensure.Eq<Product>(x => x.ShelfNumber, shelfNumber))
              .List<Product>();
      }

It’s pretty simple to use and is easily refactored. You may be able to add your *extensions* to a namespace: NHibernate.Criterion and call your class Restrictions, but I didn’t try this (I don’t like mixing namespaces). Let me know what you think!

Flattr this!

Fluent NHibernate error: Unable to cast object of type ‘Oracle.DataAccess.Client.OracleConnection’ to type ‘System.Data.Common.DbConnection’.

After developing an application in Fluent NHibernate, I’ve received the following Error and Stack Trace:

Unable to cast object of type 'Oracle.DataAccess.Client.OracleConnection' to type 'System.Data.Common.DbConnection'. 
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.InvalidCastException: Unable to cast object of type 'Oracle.DataAccess.Client.OracleConnection' to type 'System.Data.Common.DbConnection'.

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.  

Stack Trace: 


[InvalidCastException: Unable to cast object of type 'Oracle.DataAccess.Client.OracleConnection' to type 'System.Data.Common.DbConnection'.]
   NHibernate.Tool.hbm2ddl.SuppliedConnectionProviderConnectionHelper.Prepare() +43
   NHibernate.Tool.hbm2ddl.SchemaMetadataUpdater.GetReservedWords(Dialect dialect, IConnectionHelper connectionHelper) +65
   NHibernate.Tool.hbm2ddl.SchemaMetadataUpdater.Update(ISessionFactory sessionFactory) +80
   NHibernate.Impl.SessionFactoryImpl..ctor(Configuration cfg, IMapping mapping, Settings settings, EventListeners listeners) +598
   NHibernate.Cfg.Configuration.BuildSessionFactory() +87
   FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory() +49

[FluentConfigurationException: An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.

]
   FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory() +69
   ETeacherWeb.DataAccess.DataSession.get_SessionFactory() +641
   ETeacherWeb.HttpModules.NHibernateSessionModule.<Init>b__0(Object , EventArgs ) +8
   System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +68
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75

The server is running Oracle 9.2 client, without ODP.NET. In order to fix this, I copied version 9.2 libraries to the server.

The libraries required were:
Oracle.DataAccess.dll (application/bin folder)
OraOps9.dll (oracle/9.2/bin)

The above directories will of course be different for you, but they should help. Also, the Oracle bin folder must be accessible via the %PATH% variable.

If possible, the much easier fix is to install a full Oracle client, including ODP.NET.

Flattr this!

ArgumentOutOfRangeException : Index was out of range. Fluent NHibernate commit

I came across the error

ArgumentOutOfRangeException : Index was out of range.

while trying to perform a commit on a single entity with 4 nested entities and 3 properties.

I tracked the problem down to one two entities: Country and “Post” which I will refer to as Location.

The objects looked like:

public class Location
{
...
        // public virtual string ContactCountry { get; set; }
...
        public virtual Country Country { get; set; }
}

public class Country
{
...       
        public virtual IList<Location> Locations { get; set; }
}

The problem came from the Fluent NHibernate mapping referring to a property on the COUNTRY_CODE field and specifying a HasMany relationship to Country on the same column.

This is an oddity, for sure. But, the better (object-oriented and proper) way to do it would be to change ContactCountry to a getter and return the Country.Code property (if ContactCountry is used elsewhere). Or, call Location.Country.Code only.

Removing the property and mapping for ContactCountry fixed the problem.

Flattr this!

log4net configuration in .NET 3.5

Step 1 Add Reference
Right click on the “References” folder and choose add reference. Browse to the location of log4net and add it to the project.

Step 2 Add Config Section Reference
In web.config, add a reference to the log4net config section handler. This will look like:

<?xml version="1.0"?>
<configuration>
 <configSections>
  <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
 <!-- other sections/sectionGroups -->
 </configSections>
</configuration>

Step 3 Add the log4net configuration Section
All that is left is to add the actual configuration. There are an unlimited number of ways this can be done, with numerous different logs and log types. An example configuration follows. This configuration creates a log for NHibernate with a max size of 8MB, a rolling file (max size 3MB), and a a console log. Anything logged to the logger named NHibernate.SQL will record only if they are ERROR level or higher. Anything logged without specifying a logger name is logged to root, and only goes to the console and rolling file.

 <log4net>
  <appender name="NHibernateFileLog" type="log4net.Appender.RollingFileAppender,log4net">
   <file value="logs/nhibernate.txt"/>
   <appendToFile value="true"/>
   <rollingStyle value="Size"/>
   <maxSizeRollBackups value="0"/>
   <maximumFileSize value="8MB"/>
   <staticLogFileName value="true"/>
   <layout type="log4net.Layout.PatternLayout,log4net">
    <conversionPattern value="%d [%t] %-5p %c - %m%n"/>
   </layout>
  </appender>
  <appender name="console" type="log4net.Appender.ConsoleAppender, log4net">
   <layout type="log4net.Layout.PatternLayout,log4net">
    <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p %c{1}:%L - %m%n"/>
   </layout>
  </appender>
    <!-- log4net uses 5 levels, namely DEBUG, INFO, WARN, ERROR and FATAL. -->
  <appender name="rollingFile" type="log4net.Appender.RollingFileAppender,log4net">
   <param name="File" value="logs/RollingLog.txt"/>
   <param name="AppendToFile" value="false"/>
   <param name="RollingStyle" value="Date"/>
   <param name="DatePattern" value="yyyy.MM.dd"/>
   <param name="StaticLogFileName" value="true"/>
   <maximumFileSize value="3MB"/>
   <maxSizeRollBackups value="0"/>
   <staticLogFileName value="true"/>
   <layout type="log4net.Layout.PatternLayout,log4net">
    <param name="ConversionPattern" value="%d{HH:mm:ss.fff} [%t] %-5p %c - %m%n"/>
   </layout>
  </appender>
  <logger name="NHibernate.SQL" additivity="false">
   <level value="ERROR"/>
   <appender-ref ref="NHibernateFileLog"/>
   <appender-ref ref="console"/>
   <appender-ref ref="rollingFile"/>
  </logger>
  <root>
   <level value="ERROR"/>
   <appender-ref ref="console"/>
      <appender-ref ref="rollingFile"/>
   <!--Uncomment the following appender for verbose output (degrades performance)-->
   <!--<appender-ref ref="rollingFile"/>-->
  </root>
 </log4net>
 

Step 4
Now, all that’s left is to call log4net’s configure method before any other (read: **error prone**) code is called.
For example, in an ASP.NET Web application, you could add this to Global.asax:

public class GlobalAsax : HttpApplication
{
    void Application_Start(object sender, EventArgs e)
    {
        log4net.Config.XmlConfigurator.Configure();
    }
}

Other logging options include database, mail, net, access.

For more information and examples on log4net, visit http://logging.apache.org/log4net/release/config-examples.html

An example Logger implementation in C#:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using log4net;
using log4net.Config;
namespace ETeacherWeb.Common
{
    /// <summary>
    /// Logger class for log4net programmatic logging
    /// </summary>
    public static class Logger
    {
        /// <summary>
        /// Severity/Level of the log entry
        /// </summary>
        public enum LogLevel
        {
            DEBUG = 1,
            ERROR,
            FATAL,
            INFO,
            WARN
        }
        #region Members
        private static readonly ILog logger = LogManager.GetLogger(typeof(Logger));
        #endregion
        #region Constructors
        static Logger()
        {
            XmlConfigurator.Configure();
        }
        #endregion
        #region Methods
        /// <summary>
        /// Write a string to the log with a specified level of severity
        /// </summary>
        /// <param name="logLevel">The severity of the log entry</param>
        /// <param name="log">The log entry</param>
        public static void WriteLog(LogLevel logLevel, String log)
        {
            if (logLevel.Equals(LogLevel.DEBUG))
            {
                logger.Debug(log);
            }
            else if (logLevel.Equals(LogLevel.ERROR))
            {
                logger.Error(log);
            }
            else if (logLevel.Equals(LogLevel.FATAL))
            {
                logger.Fatal(log);
            }
            else if (logLevel.Equals(LogLevel.INFO))
            {
                logger.Info(log);
            }
            else if (logLevel.Equals(LogLevel.WARN))
            {
                logger.Warn(log);
            }
        }
        #endregion
    }
}
 

To use the above code, you would call

 Logger.WriteLog(LogLevel.DEBUG, "The expected logic failed validation");

Flattr this!