I got some trouble with NHibernate. I created the following interceptor to update audit fields before saving changes to the database:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class NHibernateInterceptor : NHib.EmptyInterceptor | |
{ | |
private readonly ILogger _logger; | |
private readonly IUserFactory _userFactory; | |
public NHibernateInterceptor(ILogger logger, IUserFactory userFactory) | |
{ | |
_logger= logger; | |
_userFactory = userFactory; | |
} | |
public override bool OnFlushDirty(object entity, object id, object[] currentState, object[] previousState, string[] propertyNames, global::NHibernate.Type.IType[] types) | |
{ | |
if (entity is IAuditable auditable) | |
{ | |
var updatedByIndex = Array.IndexOf(propertyNames, nameof(IAuditable.UpdatedBy)); | |
var updatedOnIndex = Array.IndexOf(propertyNames, nameof(IAuditable.UpdatedOn)); | |
currentState[updatedByIndex] = auditable.UpdatedBy = _userFactory.GetCurrentUser().Id; | |
currentState[updatedOnIndex] = auditable.UpdatedOn = DateTime.Now; | |
} | |
return base.OnFlushDirty(entity, id, currentState, previousState, propertyNames, types); | |
} | |
public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, global::NHibernate.Type.IType[] types) | |
{ | |
if (entity is IAuditable auditable) | |
{ | |
var createdByIndex = Array.IndexOf(propertyNames, nameof(IAuditable.CreatedBy)); | |
var createdOnIndex = Array.IndexOf(propertyNames, nameof(IAuditable.CreatedOn)); | |
var updatedByIndex = Array.IndexOf(propertyNames, nameof(IAuditable.UpdatedBy)); | |
var updatedOnIndex = Array.IndexOf(propertyNames, nameof(IAuditable.UpdatedOn)); | |
var now = DateTime.UtcNow; | |
state[createdByIndex] = auditable.CreatedBy = _userFactory.GetCurrentUser().Id; | |
state[createdOnIndex] = auditable.CreatedOn = now; | |
state[updatedByIndex] = auditable.UpdatedBy = _userFactory.GetCurrentUser().Id; | |
state[updatedOnIndex] = auditable.UpdatedOn = now; | |
} | |
return base.OnSave(entity, id, state, propertyNames, types); | |
} | |
} |
This interceptor does it’s job for simple objects but seems not to work for inherited objects:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class CategoryMap : ClassMap<Category> | |
{ | |
public CategoryMap() | |
{ | |
Table("Categories"); | |
Id(p => p.Id, "CategoryID"); | |
Map(p => p.CategoryName).Not.Nullable(); | |
Map(p => p.Description); | |
Map(p => p.Picture); | |
Map(e => e.CreatedOn).Not.Nullable(); | |
Map(e => e.CreatedBy).Not.Nullable(); | |
Map(e => e.UpdatedOn).Not.Nullable(); | |
Map(e => e.UpdatedBy).Not.Nullable(); | |
} | |
} |
To get it working I had to change the interceptor to always return true in the OnSave and OnFlushDirty methods:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class NHibernateInterceptor : NHib.EmptyInterceptor | |
{ | |
private readonly ILogger _logger; | |
private readonly IUserFactory _userFactory; | |
public NHibernateInterceptor(ILogger logger, IUserFactory userFactory) | |
{ | |
_logger= logger; | |
_userFactory = userFactory; | |
} | |
public override bool OnFlushDirty(object entity, object id, object[] currentState, object[] previousState, string[] propertyNames, global::NHibernate.Type.IType[] types) | |
{ | |
if (entity is IAuditable auditable) | |
{ | |
var updatedByIndex = Array.IndexOf(propertyNames, nameof(IAuditable.UpdatedBy)); | |
var updatedOnIndex = Array.IndexOf(propertyNames, nameof(IAuditable.UpdatedOn)); | |
currentState[updatedByIndex] = auditable.UpdatedBy = _userFactory.GetCurrentUser().Id; | |
currentState[updatedOnIndex] = auditable.UpdatedOn = DateTime.Now; | |
//Return true to always trigger update of the fields | |
return true; | |
} | |
return base.OnFlushDirty(entity, id, currentState, previousState, propertyNames, types); | |
} | |
public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, global::NHibernate.Type.IType[] types) | |
{ | |
if (entity is IAuditable auditable) | |
{ | |
var createdByIndex = Array.IndexOf(propertyNames, nameof(IAuditable.CreatedBy)); | |
var createdOnIndex = Array.IndexOf(propertyNames, nameof(IAuditable.CreatedOn)); | |
var updatedByIndex = Array.IndexOf(propertyNames, nameof(IAuditable.UpdatedBy)); | |
var updatedOnIndex = Array.IndexOf(propertyNames, nameof(IAuditable.UpdatedOn)); | |
var now = DateTime.UtcNow; | |
state[createdByIndex] = auditable.CreatedBy = _userFactory.GetCurrentUser().Id; | |
state[createdOnIndex] = auditable.CreatedOn = now; | |
state[updatedByIndex] = auditable.UpdatedBy = _userFactory.GetCurrentUser().Id; | |
state[updatedOnIndex] = auditable.UpdatedOn = now; | |
//Return true to always trigger update of the fields | |
return true; | |
} | |
return base.OnSave(entity, id, state, propertyNames, types); | |
} | |
} | |