I am trying to configure op-locking using fluent nhibernate.
There is a lot of info out there but none seems to fit the scenario I'm in. My class and map are as follows (edited for brevity):
Entity:
public class EmailGroup : CRUDDomainObject<EmailGroup>
{
public virtual string Id { get; set; }
public virtual MailServer Server { get; set;}
public virtual string FromAddress { get; set;}
public virtual string ToAddress { get; set;}
public virtual long Version { get; set; }
}
Map:
public class EmailGroupMap : ClassMap<EmailGroup>
{
public const string TABLE_ID = "EMAILGROUP";
public const string FIELD_ID = "EMAILID";
public const string FIELD_MAIL_SERVER = "MAILSERVID";
public const string FIELD_FROM_ADDRESS = "EMLFROM";
public const string FIELD_TO_ADDRESS = "EMLTO";
public const string FIELD_VERSION = "VERSION";
public EmailGroupMap()
{
Table(TABLE_ID);
Id(x => x.Id)
.Column(FIELD_ID)
.Not.Nullable()
.GeneratedBy.Assigned()
.Length(12);
References(x => x.Server)
.Column(FIELD_MAIL_SERVER)
.NotFound.Ignore();
Map(x => x.FromAddress)
.Column(FIELD_FROM_ADDRESS)
.Not.Nullable()
.Length(120);
Map(x => x.ToAddress)
.Column(FIELD_TO_ADDRESS)
.Not.Nullable()
.Length(1000);
Version(X => X.Version)
.Column(FIELD_VERSION)
.Generated.Always()
.UnsavedValue("0")
.Access.Property();
DynamicUpdate();
OptimisticLock.Version();
}
}
All looks well to me here, but when I load the entity and modify it, the version number is not incremented. Likewise if I manually increment the version, while a session is open, I get no StaleObjectException.
Does this config look valid to the more experienced eye? If so what else could I be missing?
UPDATE:
After implementing a database managed timestamp the version column is (of course) being incremented. However NHibernate doesn't treat the row as optimistically locked. I captured the update query from the SQL server to check the where clause (truncated for brevity):
exec sp_executesql N'UPDATE [EMAILGROUP]
SET [EMLDESC] = @EMLDESC, [MAILSERVID] = @MAILSERVID, [EMLFROM] = @EMLFROM, [EMLTO] = @EMLTO, [EMLCC] = @EMLCC, [EMLBCC] = @EMLBCC
WHERE [EMAILID] = @EMAILID'
Why did you specify Generated.Always()? That tells NHibernate that this isn't a real column but instead calculated by the database. Documentation: http://nhibernate.info/doc/nh/en/index.html#mapping-generated
Remove that and it should work.
The most typical scneario for Version and SQL Server (not sure if this is your case) is the sql type timestamp (obsolete) or better rowversion. This should be mapped to C# byte[]. So these changes should solve it:
1) Version column on the server must be of type rowversion (or timestamp). Such a column is automatically updated on any changes related to current row. only one such column can exist per table
2) The entity should look like this
public class EmailGroup : CRUDDomainObject<EmailGroup>
{
...
public virtual byte[] Version { get; set; }
3) the fluent mapping code should remain as it is. It should be a job of a fluent mapper to do the tricks behind. what we need to achieve is something like this:
<version name="Version" generated="always" unsaved-value="null" type="BinaryBlob">
<column name="Version" not-null="false" sql-type="timestamp"/>
</version>
Please, see more here: http://ayende.com/blog/3946/nhibernate-mapping-concurrency
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