#nhibernate #session #transactions #repository
#nhibernate #сеанс #транзакции #репозиторий
Вопрос:
Привет, я начинаю изучать NHibernate и теперь пытаюсь создать собственный класс репозитория для NHibernate.
Я нашел много статей об этой теме. Я выбрал этот учебник: http://dotnetslackers.com/Community/blogs/antrad/archive/2008/10/25/about-repository-pattern-and-nhibernate.aspx
Я немного смущен тем, как обрабатывать транзакцию в репозитории.
У меня есть интерфейс репозитория:
public interface IRepository<T> where T : class
{
void Commit();
void RollBack();
void BeginTransaction();
//...
}
Мой вопрос в том, почему хорошо начинать транзакцию в классе конструктора репозитория.
Эта часть кода взята из блога сверху.
public class NHibRepository<T> :IRepository<T>
where T : class
{
#region Private fields
private ISession _session;
#endregion
#region Constructor
public NHibRepository(ISession session)
{
_session = NHibernateSessionManager.Instance.GetSession();
//why begin transaction where???
_session.BeginTransaction();
}
#endregion
#region Trans
public void Commit()
{
if (session.Transaction.IsActive)
{
session.Transaction.Commit();
}
}
public void Rollback()
{
if (session.Transaction.IsActive)
{
session.Transaction.Rollback();
session.Clear();
}
}
public void BeginTransaction()
{
Rollback();
session.BeginTransaction();
}
#endregion
}
Я обновляю этот репозиторий и теперь использую это решение:
public class NHibRepository<T> :IRepository<T>
where T : class
{
#region Private fields
private ISession _session;
#endregion
#region Constructor
public NHibRepository(ISession session)
{
_session = session;
}
#endregion
#region Trans
public void Commit()
{
using (var trans=_session.BeginTransaction())
{
trans.Commit();
}
}
public void RollBack()
{
using (var trans = _session.BeginTransaction())
{
trans.Rollback();
}
}
public void BeginTransaction()
{
throw new NotImplementedException();
}
#endregion
}
Мой первый вопрос: какое решение лучше и почему? или что правильно, почему?
И мой второй вопрос заключается в том, как открыть сеанс для класса репозитория, теперь я использую этот вспомогательный класс:
public class NHibeHelper
{
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
InitializeSessionFactory();
return _sessionFactory;
}
}
private static void InitializeSessionFactory()
{
_sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(
@"Server=ZuesSQLEXPRESS;Database=TEST;Trusted_Connection=True;")
)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<UserMap>().Conventions.Add(DefaultCascade.All()))
.ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(true, true))
.BuildSessionFactory();
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}
При использовании подобным образом:
_userRepo = new NHibRepository<User>(NHibeHelper.OpenSession());
Ответ №1:
Рассматривая два варианта, которые вы представили для управления транзакциями, они оба на самом деле имеют проблемы.
В примере 1 транзакция не обязательно удаляется, а также вы связываете время жизни транзакции с временем жизни репозитория. Это не обязательно плохо, но если вы не используете контейнер IoC или какой-либо другой метод для обеспечения повторного создания репозитория для каждой операции с базой данных, вы сможете выполнить фиксацию только один раз, а последующие коммиты, вероятно, завершатся неудачей.
К сожалению, пример 2 не будет работать вообще — при фиксации / откате вы каждый раз запускаете новую транзакцию. Особенно в случае отката, если это было вызвано из внешнего кода в контексте транзакции, все, что вы сделаете, это запустите новую вложенную транзакцию, а затем немедленно откатите ее. К сожалению, не очень полезно.
Мое личное решение вашего первого вопроса — вывести управление транзакциями из репозитория, используя внешние транзакции в системе.Транзакции. Таким образом, у вас все еще есть разделение, поскольку внешние транзакции не зависят от поставщика, но также у вас есть больше контроля над тем, какие из многих операций репозитория, которые вы можете выполнять, будут задействованы в транзакции, а не всегда использовать модель с одной транзакцией на операцию. Смотрите http://msdn.microsoft.com/en-us/magazine/cc163527.aspx для получения дополнительной информации.
Что касается вашего второго вопроса, я бы настоятельно рекомендовал заглянуть в контейнер IoC, такой как Castle Windsor или Unity. Вспомогательный класс, который у вас есть, намного лучше, чем перестраивать фабрику сеансов и тому подобное каждый раз, но, используя контейнер IoC, вы можете очень легко обеспечить, чтобы время жизни вашего объекта сеанса было именно таким, каким вы хотите. Например, в ряде служб WCF для каждого вызова, над которыми я работаю, я использую Castle Windsor, чтобы гарантировать, что сеанс создается (и вводится в репозиторий), когда начинается операция WCF, и удаляется (вместе с репозиторием), когда она заканчивается.