Hello altogether,

Environment: nHibernate version: 1.2.1.4000
Repository: M$ SQL Database Server 2005
VS2008, .NET 3.5, C#


Situation:
I'm trying to implement concurrency handling using nHibernate. I have
a 'timestamp' column in a database table. As I read that nHibernate
and timestamp column from SQL Server 2005 don't get along pretty well,
I've implemented a custom IUserVersionType class and used it in my
mapping like this:
Code:
<...>
<class name="MyTestProject.DomainModel.BusinessObjects.Document,
MyTestProject.DomainModel"
      table="Documents" dynamic-insert="true" dynamic-update="true"
lazy="false" optimistic-lock="version">
   <id name="Number" column="DocumentNumber" type="Int64" unsaved-
value="0">
      <generator class="identity"/>
   </id>
   <version name="RecordVersion" column="RecordTimestamp"
generated="always" unsaved-value="null"
 
type="MyTestProject.DomainModel.nHibernate.UserTypes.SqlTimestamp,
MyTestProject.DomainModel.nHibernate"/>
<...>


SqlTimestamp implementation:
Code:
   public class SqlTimestamp :
      IUserVersionType
   {
      #region IUserVersionType MEMBERS

      public object Next( object current, ISessionImplementor
session )
      {
         return current;
      }

      public object Seed( ISessionImplementor session )
      {
         return new byte[ 8 ];
      }

      #endregion //IUserVersionType MEMBERS

      #region IUserType MEMBERS

      public object Assemble( object cached, object owner )
      {
         return this.DeepCopy( cached );
      }

      public object DeepCopy( object value )
      {
         return value;
      }

      public object Disassemble( object value )
      {
         return this.DeepCopy( value );
      }

      public int GetHashCode( object x )
      {
         return x.GetHashCode();
      }

      public bool IsMutable
      {
         get { return false; }
      }

      public object NullSafeGet( IDataReader rs, string[] names,
object owner )
      {
         return rs.GetValue( rs.GetOrdinal( names[ 0 ] ) );
      }

      public void NullSafeSet( IDbCommand cmd, object value, int
index )
      {
         NHibernateUtil.Binary.NullSafeSet( cmd, value, index );
      }

      public object Replace( object original, object target, object
owner )
      {
         return original;
      }

      public Type ReturnedType
      {
         get { return typeof( byte[] ); }
      }

      public SqlType[] SqlTypes
      {
         get
         {
            SqlType[] types = new SqlType[ 1 ]{ new
SqlType( DbType.Binary ) };
            return types;
         }
      }

      #endregion //IUserType MEMBERS

      #region IComparer MEMBERS

      public int Compare( object x, object y )
      {
         byte[] xbytes = (byte[])x;
         byte[] ybytes = (byte[])y;

         if ( xbytes.Length < ybytes.Length )
         {
            return -1;
         }

         if ( xbytes.Length > ybytes.Length )
         {
            return 1;
         }

         for ( int i = 0; i < xbytes.Length; i++ )
         {
            if ( xbytes[ i ] < ybytes[ i ] )
            {
               return -1;
            }
            if ( xbytes[ i ] > ybytes[ i ] )
            {
               return 1;
            }
         }

         return 0;
      }

      bool IUserType.Equals( object x, object y )
      {
         return ( x == y );
      }

      #endregion //IComparer MEMBERS
   }


Problem:
[RecordTimestamp] value is correctly loaded and handled by nHibernate
with this implementation, BUT using 'dynamic-insert/update'
attribute(s) nHibernate tries to update <version> property every time
and thus my operation fails with an error:
System.Data.SqlClient.SqlException: Cannot update a timestamp column.

What I want to accomplish:
Set property [RecordVersion] non-updatable via code.

Acknowledgments:
- override IInterceptor.FindDirty(..) -- IMHO to difficult for this
case
- not use 'dynamic-insert/update' attribute(s) -- out of the question

Is there an elegant way in this situation to kindly tell nHibernate to
never try to update [RecordTimestamp] value in the database?

Regards,
Mindaugas

P.S. By debugging my audit interceptor (implementation of
EmptyInterceptor), I've noticed, that [RecordVersion] property value
is the same for current state object and previous state object
(setting breakpoint at FindDirty(..) and OnFlushDirty(..)). So why
nHibernate includes it into an update statement? And why isn't
overridden method Equals(..) or Compare(..) (in SqlTimestamp) get
called (to decide is values are the same before including property
into "to update" list)?

* * * UPDATE * * *

Debugging NHibernate project,
NHibernate.Persister.Entity.AbstractEntityPersister:
ScheduledUpdate calls AbstractEntityPersister's method Update(..) on
versionable object flush. As my class is marked for 'dynamic-update',
Persister retrieves properties to update, thus calling it's
GetPropertiesToUpdate(..). This method picks properties that are dirty
(=changed) AND (if persisting object is versionable - in my case it
is) adds [RecordVersion] property for update, although my versionable
property has 'generated=always'... :|

Update: I've noticed, that AbstractEntityPersister's property
[PropertyUpdateability] correctly returns properties that can be
updated, i.e. [RecordVersion] property is marked as 'non-updatable'.
But method GetPropertiesToUpdate(..) ignores that updatable properties
collection for versioned property, although checks updatability for
dirty properties.. :/

-- 
You received this message because you are subscribed to the Google Groups 
"nhusers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/nhusers?hl=en.

Reply via email to