I am trying to use NHibernate to map a legacy database which uses a decimal(9,0)
(~32 bit integer) "version property" (for optimistic locking of each row).
The fields are declared nullable in the database, and in section 5.1.7 of the NHibernate reference manual it states:
Version numbers may be of type Int64, Int32, Int16, Ticks, Timestamp, or TimeSpan (or their nullable counterparts in .NET 2.0).
... so I mapped them as int?
version properties.
My mapping document looks similar to this:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping namespace="Model" assembly="Model" xmlns="urn:nhibernate-mapping-2.2">
<class name="Bar" lazy="true" table="`BAR`" schema="`dbo`">
<id name="ID" access="property" column="`ID`">
<generator class="assigned" />
</id>
<version name="Version" column="`VERSION`" type="int?" />
<!-- ... etc ... -->
</class>
</hibernate-mapping>
However, at runtime, this fails miserably with an exception similar to:
failed: NHibernate.MappingException : Could not compile the mapping document: Model.Mappings.Bar.hbm.xml
----> NHibernate.MappingException : Could not determine type for: Model.int?, for columns: NHibernate.Mapping.Column(VERSION)
at NHibernate.Cfg.Configuration.LogAndThrow(Exception exception)
at NHibernate.Cfg.Configuration.AddValidatedDocument(NamedXmlDocument doc)
at NHibernate.Cfg.Configuration.ProcessMappingsQueue()
at NHibernate.Cfg.Configuration.AddDocumentThroughQueue(NamedXmlDocument document)
at NHibernate.Cfg.Configuration.AddXmlReader(XmlReader hbmReader, String name)
at NHibernate.Cfg.Configuration.AddInputStream(Stream xmlInputStream, String name)
at NHibernate.Cfg.Configuration.AddResource(String path, Assembly assembly)
at NHibernate.Cfg.Configuration.AddAssembly(Assembly assembly)
Core\DAOUtils.cs(33,0): at Core.DAOUtils.OpenSession()
test\cs\FooTest.cs(65,0): at DataModel.Tests.FooTest.TestSelectStar()
--MappingException
at NHibernate.Mapping.SimpleValue.get_Type()
at NHibernate.Cfg.XmlHbmBinding.RootClassBinder.BindProperty(HbmVersion versionSchema, Property property, IDictionary`2 inheritedMetas)
at NHibernate.Cfg.XmlHbmBinding.RootClassBinder.BindVersion(HbmVersion versionSchema, PersistentClass rootClass, Table table, IDictionary`2 inheritedMetas)
at NHibernate.Cfg.XmlHbmBinding.RootClassBinder.Bind(XmlNode node, HbmClass classSchema, IDictionary`2 inheritedMetas)
at NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.AddRootClasses(XmlNode parentNode, IDictionary`2 inheritedMetas)
at NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.Bind(XmlNode node)
at NHibernate.Cfg.Configuration.AddValidatedDocument(NamedXmlDocument doc)
Is there some trick to correctly using version properties with NHibernate, or am I doing something wrong?
One odd thing to me is that NHibernate seems to be appending my assembly/namespace name to the beginning of the int?
type, which seems wrong.
Edit:
After changing the mapping files to use int
instead of int?
I am now hitting the following exception:
System.InvalidCastException : Unable to cast object of type 'NHibernate.Type.DecimalType' to type 'NHibernate.Type.IVersionType'.
at NHibernate.Tuple.PropertyFactory.BuildVersionProperty(Property property, Boolean lazyAvailable)
at NHibernate.Tuple.Entity.EntityMetamodel..ctor(PersistentClass persistentClass, ISessionFactoryImplementor sessionFactory)
at NHibernate.Persister.Entity.AbstractEntityPersister..ctor(PersistentClass persistentClass, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory)
at NHibernate.Persister.Entity.SingleTableEntityPersister..ctor(PersistentClass persistentClass, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory, IMapping mapping)
at NHibernate.Persister.PersisterFactory.CreateClassPersister(PersistentClass model, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory, IMapping cfg)
at NHibernate.Impl.SessionFactoryImpl..ctor(Configuration cfg, IMapping mapping, Settings settings, EventListeners listeners)
at NHibernate.Cfg.Configuration.BuildSessionFactory()
Unfortunately, NHibernate isn't giving me a very detailed error message here (it would be nice to know which class out of the hundreds of classes is throwing this), and I haven't been able to convince log4net to work, so I'm at a loss for the moment.
Edit #2:
To summarize the "tricks":
- Assuming integer version properties, (and even if they are declared
decimal
in the database) make sure all version properties are declared asint
in the mapping XML. (though it should work ifint?
is declared in the POCO) - Make sure no version properties are set as a
decimal
type (either in the POCOs or the mapping XML)