Класс для работы с доменными сущностями:
public class Repository<T> : IDisposable
where T : BaseEntity
{
private readonly ISession _session; // сессия открыта пока существует репозиторий
public Repository()
{
_session = Db.OpenSession();
}
public T Get(int id)
{
return Transaction(() => _session.Get<T>(id));
}
public IList<T> GetAll()
{
return Transaction(() => _session.CreateCriteria<T>().List<T>());
}
public int Add(T entity)
{
return Transaction(() => (int) _session.Save(entity));
}
public void Remove(T entity)
{
Transaction(() => _session.Delete(entity));
}
public bool Remove(int id)
{
var query = string.Format("from {0} where Id = {1}", typeof (T).Name, id);
return Transaction(() => _session.Delete(query)) == 1;
}
private void Transaction(Action command)
{
using (var transaction = _session.BeginTransaction())
{
command();
transaction.Commit();
}
}
private TOut Transaction<TOut>(Func<TOut> command)
{
using (var transaction = _session.BeginTransaction())
{
var result = command();
transaction.Commit();
return result;
}
}
public void Dispose()
{
_session.Close();
}
}
Есть сущность Entity : BaseEntity, у которой помимо ID задан unique constraint по полю TIME.
Пишу тест (NUnit):
[Test]
public void EntityRepository()
{
using (var repository = new Repository<Entity>())
{
var time = DateTime.UtcNow;
var entity = new Entity(time);
var badEntity = new Entity(time); // сущность с таким же уникальным полем
var id = repository.Add(entity);
Assert.AreNotEqual(default(int), entity.Id); // success (поле ID присвоено)
var get = repository.Get(id);
Assert.AreEqual(entity, get); // success (в рамках одной сессии это будет выполняться)
Assert.Throws<GenericADOException>(() => repository.Add(badEntity)); // success (при добавлении сущности с таким же значением уникального поля выпало исключение)
var isRemoved = repository.Remove(id); // NHibernate.AssertionFailure : null id in ... (больше с сессией работать невозможно)
Assert.IsTrue(isRemoved);
}
}
При удалении строки Assert.Throws(() => repository.Add(badEntity)); тест выполняется полностью.
Почему так происходит? Какими должны быть мои действия после выпадения исключения? Почему ITransaction.Rollback(), который вызывается неявно в конце using не откатывает состояние ISession?
P.S.: при отладке теста заметил, что после выполнения строки с исключением _session.Statistics.EntityCount == 2, хотя я ожидаю, что будет 1. Это баг NHibernate?