What is the correct way to map an timestamp column in an SQL Server 2008 database using NHibernate's new code based mappings?
I have the property in my class defined as byte[] and I'm using the following mapping in my ClassMapping file:
Version(x => x.RowVersion, mapping =>
mapping.Generated(VersionGeneration.Always));
However, NHibernate is expecting an integer based on this mapping (throws an exception on inserts). If I explicitly specify the mapping type as byte[], I get an exception stating: "System.ArgumentOutOfRangeException: Expected type implementing IUserVersionType Parameter name: persistentType".
What is the proper way to map an auto updating timestamp column with the new NHibernate code based mappings?
---EDIT
I think I have it narrowed down that I need to set the Type on the mapping to BinaryType (an NHibernate Type that implements IVersionType), but BinaryType doesn't have a public constructor...I think I'm out of ideas.
If you grab the NHib source code, there's a class in a test project that will help you with what you need: NHibernate.Test.VersionTest.Db.MsSQL.BinaryTimestamp. Basically, you have to give it a custom type it can use to convert the value. Be default NHib expects the value to be an int (look at seciont 5.1.7 of the nhib docs). If you use an int/bigint as your version column, you will not need a custom type.
The custom class (lifted from NHib source code):
public class BinaryTimestamp : IUserVersionType
{
#region IUserVersionType Members
public object Next(object current, ISessionImplementor session)
{
return current;
}
public object Seed(ISessionImplementor session)
{
return new byte[8];
}
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
}
public object DeepCopy(object value)
{
return value;
}
public object Disassemble(object value)
{
return 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 System.Type ReturnedType
{
get { return typeof(byte[]); }
}
public SqlType[] SqlTypes
{
get { return new[] { new SqlType(DbType.Binary, 8) }; }
}
public int Compare(object x, object y)
{
var xbytes = (byte[])x;
var ybytes = (byte[])y;
return CompareValues(xbytes, ybytes);
}
bool IUserType.Equals(object x, object y)
{
return (x == y);
}
#endregion
private static int CompareValues(byte[] x, byte[] y)
{
if (x.Length < y.Length)
{
return -1;
}
if (x.Length > y.Length)
{
return 1;
}
for (int i = 0; i < x.Length; i++)
{
if (x[i] < y[i])
{
return -1;
}
if (x[i] > y[i])
{
return 1;
}
}
return 0;
}
public static bool Equals(byte[] x, byte[] y)
{
return CompareValues(x, y) == 0;
}
}
A sample mapping using that class:
public class Car
{
public virtual long CarId { get; set; }
public virtual string Name { get; set; }
public virtual byte[] LastModified { get; set; }
public override string ToString()
{
return string.Format("Id: {0}, Name: {1}, Last Modified: {2}", CarId, Name, LastModified);
}
}
public class CarMap : ClassMapping<Car>
{
public CarMap()
{
Table("Cars");
Id(car => car.CarId, mapper => mapper.Generator(Generators.Identity));
Property(car => car.Name);
Version(car => car.LastModified, mapper =>
{
mapper.Generated(VersionGeneration.Always);
mapper.Type<BinaryTimestamp>();
});
}
}
We also are using byte[] Version { get; }
for version implementation.
Here is out mappings:
Version(x => x.Version)
.Nullable()
.CustomSqlType("timestamp")
.Generated.Always()
;
Also for more details please see this link
In the case of a ConventionModelMapper I ended up using the following..
ConventionModelMapper mapper = new ConventionModelMapper();
//...other mappings
mapper.Class<Entity>(map => map.Version(e => e.Revision, m =>
{
m.Generated(VersionGeneration.Always);
m.UnsavedValue(null);
m.Type(new BinaryBlobType());
m.Column(c =>
{
c.SqlType("timestamp");
c.NotNullable(false);
});
}));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With