views:

61

answers:

1

I would like to get NHibernate to roll back the identifier of any entities saved during a transaction if the transaction is aborted. I am using NHibernate 2.1.0, and it doesn't do this by default. I came up with the following solution, which works up to a point:

public class RevertIdentifiersEventListener : DefaultSaveEventListener
{
    private class RevertIdentiferSynchronization : ISynchronization
    {
        private IClassMetadata _classMetadata;
        private object _entity;
        private object _originalIdentifier;

        public RevertIdentiferSynchronization(IClassMetadata classMetadata, object entity)
        {
            _classMetadata = classMetadata;
            _entity = entity;
            _originalIdentifier = classMetadata.GetIdentifier(entity, NHibernate.EntityMode.Poco);
        }

        #region ISynchronization Members

        public void AfterCompletion(bool success)
        {
            if (success == false)
            {
                _classMetadata.SetIdentifier(_entity, _originalIdentifier, NHibernate.EntityMode.Poco);
            }
        }

        public void BeforeCompletion()
        {
        }

        #endregion
    }


    protected override object EntityIsTransient(SaveOrUpdateEvent @event)
    {
        IEntityPersister entityPersister = @event.Session.GetEntityPersister(@event.EntityName, @event.Entity);
        if (entityPersister.HasIdentifierProperty)
        {
            RevertIdentiferSynchronization revertIdentiferSynchronization = new RevertIdentiferSynchronization(entityPersister.ClassMetadata, @event.Entity);
            if (@event.Session.Transaction.IsActive)
            {
                @event.Session.Transaction.RegisterSynchronization(revertIdentiferSynchronization);
            }
        }
        return base.EntityIsTransient(@event);
    }

}

The problem is that if the save of an entity cascades the save to an associated entity, that entity's identifier is not rolled back. EntityIsTransient above is not called for these entities, it is only called for the ones on which Save is explicitly called.

Any suggestions on how to get it working when cascading too?

A: 

I am not sure in what circumstances you want to abort a transaction, but I am assuming you need something like this:

    using( var transaction = SessionFactory.GetSession().BeginTransaction())
    {
        try
        {
           session.Update(entity); // throws exception for some reason
        }
        catch(Exception ex)
        {
            transaction.Rollback(); // rollback the transaction
        }
    }
reach4thelasers