I have a very simple table in an SQL server that is mapped in a C# POCO object, with the following code:
public class DataTable : Entity<Guid>
{
public virtual int itemId { get; set; }
public virtual string data { get; set; }
public virtual DateTime lastDate { get; set; }
public virtual string lastUser { get; set; }
}
public class DataTableMap : ClassMap<DataTable>
{
public DataTableMap()
{
Map(x => x.itemId);
Map(x => x.data);
Map(x => x.lastDate);
Map(x => x.lastUser);
}
}
I would like to track changes, using an Hibernate interceptor, so that I can add some info in another SQL server table is some of the value occurs (Eg data is empty or lastUser is Admin).
I have implemented a simple interceptor:
public class TrackingEntityInterceptor : EmptyInterceptor
{
public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types)
{
var persistentObject = entity as IPersistentObject;
if (persistentObject != null)
{
persistentObject.OnSave();
}
return false;
}
public override SqlString OnPrepareStatement(SqlString sql)
{
ApplicationBlocks.Logging.Log.Debug($"Executing sql: {sql.ToString()}");
return sql;
}
}
But I am not able to access the values of the Entity that is saved, nor I am able to active the interceptor only when that specific Entity is saved.
Is there a way to have a function called when that Entity is being saved, that allows me to inspect the values that are about to be saved and eventually apply some other changes to the DB ?
Yeah. Like David said, an EventListener is your best bet here. Here's an example of one I use to stamp all entities with audit field values.
namespace NHibernate.Extensions.EventListeners
{
public class EventListener : IPreInsertEventListener, IPreUpdateEventListener
{
private readonly IStamper _stamper;
public EventListener() : this(new Stamper())
{
}
public EventListener(IStamper stamper)
{
_stamper = stamper;
}
public bool OnPreInsert(PreInsertEvent @event)
{
_stamper.Insert(@event.Entity as IStampedEntity, @event.State, @event.Persister);
return false;
}
public Task<bool> OnPreInsertAsync(PreInsertEvent @event, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public bool OnPreUpdate(PreUpdateEvent @event)
{
_stamper.Update(@event.Entity as IStampedEntity, @event.OldState, @event.State, @event.Persister);
return false;
}
public Task<bool> OnPreUpdateAsync(PreUpdateEvent @event, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
}
and then the stamper
public class Stamper : IStamper
{
private const string CreateUser = "CreateUser";
private const string CreateDate = "CreateDate";
private const string LastUpdateUser = "LastUpdateUser";
private const string LastUpdateDate = "LastUpdateDate";
public void Insert(IStampedEntity entity, object[] state, IEntityPersister persister)
{
if (entity == null)
return;
SetCreate(entity, state, persister);
SetChange(entity, state, persister);
}
public void Update(IStampedEntity entity, object[] oldState, object[] state, IEntityPersister persister)
{
if (entity == null)
return;
SetChange(entity, state, persister);
}
private void SetCreate(IStampedEntity entity, object[] state, IEntityPersister persister)
{
entity.CreateUser = GetUserName();
SetState(persister, state, CreateUser, entity.CreateUser);
entity.CreateDate = DateTime.UtcNow;
SetState(persister, state, CreateDate, entity.CreateDate);
}
private void SetChange(IStampedEntity entity, object[] state, IEntityPersister persister)
{
entity.LastUpdateUser = GetUserName();
SetState(persister, state, LastUpdateUser, entity.LastUpdateUser);
entity.LastUpdateDate = DateTime.UtcNow;
SetState(persister, state, LastUpdateDate, entity.LastUpdateDate);
}
private void SetState(IEntityPersister persister, IList<object> state, string propertyName, object value)
{
var index = GetIndex(persister, propertyName);
if (index == -1)
return;
state[index] = value;
}
private int GetIndex(IEntityPersister persister, string propertyName)
{
return Array.IndexOf(persister.PropertyNames, propertyName);
}
private string GetUserName()
{
return HttpContext.Current != null
? HttpContext.Current.User.Identity.Name
: WindowsIdentity.GetCurrent().Name;
}
}
PreInsert is going to run before the changes are flushed to the DB and within the transaction. You probably want to use the PostInsert/PostUpdate to send your updates to a different database.
Probably a better scheme for updating a different database would be using a service bus to notify a subscriber that a change was made and then have the subscriber make the change to the external database.
From the documentation , it looks like you cannot apply interception on a type basis, but you must filter in the implemented methods:
if (entity is IAuditable) {
}
From the signature of the FlushDirty()
method you can access the previous and current state of the entity:
public override bool OnFlushDirty(object entity,
object id,
object[] currentState,
object[] previousState,
string[] propertyNames,
IType[] types)
The other thing to consider here is that event listeners are, arguably, the preferred way to handle this aspect of behaviour in NHibernate:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.