@Dem0n13

NHibernate: почему ISession не работает после исключения GenericADOException?

Класс для работы с доменными сущностями:
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?
  • Вопрос задан
  • 2706 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы