Search code examples
nhibernatenhibernate-mapping

NHibernate Named Query - How can I ignore properties whose mappings are defined by formulas?


Can I configure an NHibernate named procedure to skip certain properties when loading objects? These are not columns in the table that I want to ignore; they are properties on the object that don't correspond to columns, and I want to leave them as null when using the named procedure. (There are other situations where I do want to populate these properties).

I have an Inspection object which has many properties read from a SQL table, and one property computed using a formula:

...
<property name="InspectionDate" type="DateTime" not-null="false" />
<property name="LastCompletedDate" type="DateTime" not-null="false" 
            formula="(select max(.....long formula.....)"/>
...

This works fine when NHibernate loads an inspection. The problem comes when I use a stored procedure to get a list of Inspections:

<sql-query name="GetOverdueInspection">
      <return class="Inspection" />
      exec dbo.usp_GetOverdueInspection :dueDate
</sql-query>

This throws exception:

10011 [10] INFO NHibernate.Loader.Loader (null) - exec dbo.usp_GetOverdueInspection @p0
NHibernate: exec dbo.usp_GetOverdueInspection @p0; @p0 = '4/29/2013 7:23:41 PM'10846 [10] 
WARN NHibernate.Util.ADOExceptionReporter (null) - System.ArgumentNullException: Value cannot be null.
Parameter name: fieldName
   at System.Data.SqlClient.SqlDataReader.GetOrdinal(String name)
   at NHibernate.Driver.NHybridDataReader.GetOrdinal(String name)
   at NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String name)
   at NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String[] names, ISessionImplementor session, Object owner)
   at NHibernate.Type.AbstractType.Hydrate(IDataReader rs, String[] names, ISessionImplementor session, Object owner)
   at NHibernate.Persister.Entity.AbstractEntityPersister.Hydrate(IDataReader rs, Object id, Object obj, ILoadable rootLoadable, String[][] suffixedPropertyColumns, Boolean allProperties, ISessionImplementor session)
   at NHibernate.Loader.Loader.LoadFromResultSet(IDataReader rs, Int32 i, Object obj, String instanceClass, EntityKey key, LockMode lockMode, ILoadable rootPersister, ISessionImplementor session)
   at NHibernate.Loader.Loader.InstanceNotYetLoaded(IDataReader dr, Int32 i, ILoadable persister, EntityKey key, LockMode lockMode, EntityKey optionalObjectKey, Object optionalObject, IList hydratedObjects, ISessionImplementor session)
   at NHibernate.Loader.Loader.GetRow(IDataReader rs, ILoadable[] persisters, EntityKey[] keys, Object optionalObject, EntityKey optionalObjectKey, LockMode[] lockModes, IList hydratedObjects, ISessionImplementor session)
   at NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, LockMode[] lockModeArray, EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys, BooleanreturnProxies)
   at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies)   at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies)
   at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters)
10849 [10] ERROR NHibernate.Util.ADOExceptionReporter (null) - Value cannot be null.
Parameter name: fieldName

How can I tell NHibernate to leave the LastCompletedDate null when using this named query?


Solution

  • You may work around that by mapping your property LastCompletedDate as lazy. This is not really an answer to your question, just a workaround you could try. Of course this means that the LastCompleteDate property will always load lazily, which could be a bad thing depending on your use cases.

    <property name="LastCompletedDate" type="DateTime" not-null="false" 
            lazy="true" formula="(select max(.....long formula.....)"/>
    

    Otherwise the only way I see to achieve your intended functionality is to use a base class, sub-class it twice without adding anything, mapping one sub-class with your formula and the other one without, and work in code by casting them back to the base class for having the same type. Quite ugly with probably some bad drawbacks.