NHibernate сохранить и зафиксировать в одной транзакции

У меня есть следующий код, который создает новую закладку и добавляет к ней один или несколько тегов. Если тег еще не существует, он создается и добавляется в закладку.

Bookmark bookmark = new Bookmark();
bookmark.Title = request.Title;
bookmark.Link = request.Link;
bookmark.DateCreated = request.DateCreated;
bookmark.DateModified = request.DateCreated;
bookmark.User = _userRepository.GetUserByUsername(request.Username);

IList<Tag> myTags = _tagRepository.GetTags(request.Username);
IList<string> myTagsToString = myTags.Select(x => x.Title).ToList<string>();

foreach (var tag in request.Tags)
{
    if (myTagsToString.Contains(tag))
    {
        Tag oldTag = myTags.SingleOrDefault(x => x.Title == tag);
        bookmark.Tags.Add(oldTag);
    }
    else
    {
        Tag newTag = new Tag();
        newTag.Title = tag;
        newTag.User = _userRepository.GetUserByUsername(request.Username);
        newTag.DateCreated = request.DateCreated;
        newTag.DateModified = request.DateCreated;
        bookmark.Tags.Add(newTag);
    }
}

_bookmarkRepository.Add(bookmark);
_uow.Commit();

Я реализовал единицу работы, но не уверен, правильно ли я это сделал. Я использую метод сохранения с последующей фиксацией. Метод save вставляет закладку и теги в базу данных, а метод фиксации выполняет вставку в таблицу соединений (закладки имеют много тегов, а теги имеют много закладок).

Значит все вставлено правильно. Но если я удалю метод фиксации, закладка и теги все равно будут вставлены. Но это не вставка в соединительный стол. Означает ли это, что эти вставки не находятся в одной транзакции, поскольку фиксация не требуется для сохранения закладки и тегов в базе данных? Фиксация необходима только для сохранения связи между тегами и закладками.

РЕДАКТИРОВАТЬ:

Репозиторий

public abstract class Repository<T, TEntityKey> where T : IAggregateRoot
{
    private IUnitOfWork _uow;

    public Repository(IUnitOfWork uow)
    {
        _uow = uow;
    }

    public void Save(T entity)
    {
        SessionFactory.GetCurrentSession().SaveOrUpdate(entity);
    }

    public void Add(T entity)
    {
        SessionFactory.GetCurrentSession().Save(entity);
    }

    public void Remove(T entity)
    {
        SessionFactory.GetCurrentSession().Delete(entity);
    }

    public IEnumerable<T> FindAll()
    {
        ICriteria criteriaQuery = SessionFactory.GetCurrentSession().CreateCriteria(typeof(T));

        return (List<T>)criteriaQuery.List<T>();
    }
}

UnitOfWork

public class NHUnitOfWork : IUnitOfWork
{
    public void RegisterAmended(IAggregateRoot entity)
    {
        SessionFactory.GetCurrentSession().SaveOrUpdate(entity);
    }

    public void RegisterNew(IAggregateRoot entity)
    {
        SessionFactory.GetCurrentSession().Save(entity);
    }

    public void RegisterRemoved(IAggregateRoot entity)
    {
        SessionFactory.GetCurrentSession().Delete(entity);
    }

    public void Commit()
    {
        using (ITransaction transaction = SessionFactory.GetCurrentSession().BeginTransaction())
        {
            try
            {
                transaction.Commit();
            }
            catch (Exception ex)
            {
                transaction.Rollback();
                throw;
            }
        }
    }
}

Закладка Репозиторий

public class BookmarkRepository : Repository<Bookmark, int>, IBookmarkRepository
{
    public BookmarkRepository(IUnitOfWork uow)
        : base(uow)
    {
    }
}

ОБНОВЛЕНИЕ: я изменил NHUnitOfWork на это:

public class NHUnitOfWork : IUnitOfWork
{
    private ITransaction _transaction;

    public void RegisterAmended(IAggregateRoot entity)
    {
        SessionFactory.GetCurrentSession().SaveOrUpdate(entity);
    }

    public void RegisterNew(IAggregateRoot entity)
    {
        SessionFactory.GetCurrentSession().Save(entity);
    }

    public void RegisterRemoved(IAggregateRoot entity)
    {
        SessionFactory.GetCurrentSession().Delete(entity);
    }

    public void BeginTransaction()
    {
        _transaction = SessionFactory.GetCurrentSession().BeginTransaction();
    }

    public void Commit()
    {
        using (_transaction)
        {
            try
            {
                _transaction.Commit();
            }
            catch (Exception ex)
            {
                _transaction.Rollback();
                throw;
            }
        }
    }
}

Итак, я могу использовать это так:

_uow.BeginTransaction();

Bookmark bookmark = new Bookmark();
bookmark.Title = request.Title;
bookmark.Link = request.Link;
bookmark.DateCreated = request.DateCreated;
bookmark.DateModified = request.DateCreated;
bookmark.User = _userRepository.GetUserByUsername(request.Username);

IList<Tag> myTags = _tagRepository.GetTags(request.Username);
IList<string> myTagsToString = myTags.Select(x => x.Title).ToList<string>();

foreach (var tag in request.Tags)
{
    if (myTagsToString.Contains(tag))
    {
        Tag oldTag = myTags.SingleOrDefault(x => x.Title == tag);
        bookmark.Tags.Add(oldTag);
    }
    else
    {
        Tag newTag = new Tag();
        newTag.Title = tag;
        newTag.User = _userRepository.GetUserByUsername(request.Username);
        newTag.DateCreated = request.DateCreated;
        newTag.DateModified = request.DateCreated;
        bookmark.Tags.Add(newTag);
    }
}

_bookmarkRepository.Add(bookmark);
_uow.Commit();

Я добавил начальную транзакцию в реализацию NHUnitOfWork. Это означает, что мне нужно вызвать _uow.BeginTransaction () перед любым выбором или вставкой и в конце вызвать _uow.Commit (). Кажется, это работает, если я посмотрю на NHibernate Profiler. Если это не так, скажите пожалуйста :)


person Community    schedule 21.08.2013    source источник


Ответы (1)


Ваши Save методы не выполняются внутри транзакции, потому что вы закрываете и открываете транзакцию в Commit методе.

Правильная реализация - начать транзакцию до вызова Save.

person Daniel Hilgarth    schedule 21.08.2013
comment
Чтобы уточнить, правильной реализацией будет начало транзакции ПРЕЖДЕ ЧЕМ ВНЕДРЕНИЕ ЛЮБЫХ ИЗМЕНЕНИЙ (это включает в себя Save (), но также, например, изменение свойства Get () +, поскольку NHibernate может (с настройками по умолчанию) решить выполнить изменение базы данных в в любой момент. - person Oskar Berggren; 21.08.2013
comment
@Oskar: У вас есть пример этого? Я знаю последовательности в Oracle, которые используются для идентификатора, доступ к которому осуществляется до фиксации, но мне неизвестен какой-либо другой подобный случай. - person Daniel Hilgarth; 21.08.2013
comment
На самом деле, из NHibernate это не всегда, но с точки зрения сложного приложения может быть очень сложно контролировать, когда именно передаются модификации. Дело в том, что Commit () - это только один из возможных способов вызвать Flush (). См. Справку о том, когда Flush () может выполняться автоматически: nhforge.org/ doc / nh / en / index.html # managedata-flushing - person Oskar Berggren; 21.08.2013
comment
Из этого справочного раздела мы также узнаем, что Save () приведет к немедленному выполнению INSERT только в том случае, если требуется получить первичный ключ. - person Oskar Berggren; 21.08.2013
comment
Я обновил свой вопрос. Я надеюсь, что это правильный способ сделать это, но, похоже, он работает. - person ; 21.08.2013
comment
@OskarBerggren: Спасибо за информацию! - person Daniel Hilgarth; 21.08.2013
comment
@ Mr.nuub: ваша транзакция не откатывается, если в коде между BeginTransaction и Commit есть исключение. Фактически, ваш UoW даже не предоставляет возможности для этого. - person Daniel Hilgarth; 21.08.2013
comment
Должен ли я добавить это в команду try / catch и выполнить откат в единицу работы? попробуйте {начать транзакцию и зафиксировать} catch {если откат исключения} - person ; 21.08.2013
comment
@ Mr.nuub: Да, это то, что вам нужно сделать. - person Daniel Hilgarth; 21.08.2013