NHibernate 3.2 By Code ClassMapping for Version Property

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.

Answers


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


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>();
                                             });
    }
}

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);
                        });
                }));

Need Your Help

Heroku resets my JSON file

json node.js heroku deployment configuration

I have a node.js application that uses a simple JSON file as the model. Since this is an MVP with very limited data storage needs, I don't want to spend time designing and configuring a MongoDB dat...

Set button pressed from ViewFlipper (Android)

android button viewflipper

I'm making an Android app and have a strange problem. I need a ViewFlipper for 5 tabs. I made a layout for a page with 5 buttons. I used OnClickListener and flipping tabs works ok, but I need to

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.