У меня есть следующий код, который создает новую закладку и добавляет к ней один или несколько тегов. Если тег еще не существует, он создается и добавляется в закладку.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();
Я реализовал единицу работы, но не уверен, правильно ли я сделал это. Я использую метод сохранения, за которым следует фиксация. Метод сохранения вставляет закладку и теги в базу данных, а метод фиксации выполняет вставку в таблицу соединений (закладки имеют много тегов и тегов, имеющих много закладок).
Итак, все вставлено правильно. Но если я удалю метод фиксации, закладка и теги по-прежнему будут вставлены. Но их нет вставки в таблицу соединений. Означает ли это, что эти вставки не находятся в одной транзакции, поскольку фиксация не требуется для сохранения закладки и тегов в базе данных? Конец необходим только для сохранения связи между тегами и закладками.
РЕДАКТИРОВАТЬ:
Repository
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;
}
}
}
}
BookmarkRepository
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. Если это не так, пожалуйста, скажите мне :)
Чтобы уточнить, правильная реализация заключалась бы в том, чтобы начать транзакцию ПЕРЕД ДЕЙСТВИТЕЛЬНЫМИ ИЗМЕНЕНИЯМИ ВСЕХ (включая Save(), а также, например, Get() + модификацию свойства, поскольку NHibernate может (с настройками по умолчанию) принять решение выполнить изменение базы данных в любое время. –
@Oskar: У вас есть пример этого? Я знаю последовательности в Oracle, которые используются для идентификатора, к которому обращаются до фиксации, но я не знаю о каком-либо другом таком случае. –
Фактически , из NHibernate это на самом деле не «в любое время» - но с точки зрения сложного приложения его может быть очень сложно контролировать, когда точно передаются изменения. Дело в том, что Commit() - это всего лишь один из возможных способов запуска Flush(). См. Ссылку, если Flush() может возникать автоматически: http://nhforge.org/doc/nh/en/index.html#manipulatingdata-flushing –