Как я должен обернуть мои операторы выбора в транзакции? - PullRequest
2 голосов
/ 24 мая 2011

Я собираюсь закинуть свой сайт с помощью профилировщика nhibernate, и я получил это сообщение

Предупреждение: использование неявных транзакций не рекомендуется

http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions

Я вижу, что они присутствуют в каждом отдельном операторе select.

private readonly ISession session;

public OrderHistoryRepo(ISession session)
{
    this.session = session;
}

public void Save(OrderHistory orderHistory)
{
    session.Save(orderHistory);
}

public List<OrderHistory> GetOrderHistory(Guid Id)
{
    List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList();
    return orderHistories;
}

public void Commit()
{
    using (ITransaction transaction = session.BeginTransaction())
    {
        transaction.Commit();
    }
}

Должен ли я обернуть мою GetOrderHistory транзакцией, подобной моей с моим коммитом?

Редактировать

Как мне обернуть операторы select транзакцией?Будет ли это так?Но тогда «транзакция» никогда не используется.

    public List<OrderHistory> GetOrderHistory(Guid Id)
    {
        using (ITransaction transaction = session.BeginTransaction())
        {       

 List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList();
        return orderHistories;
        }
    }

Редактировать

Ninject (возможно, я могу использовать его, чтобы помочь мне, как я сделал с получением сеанса)

public class NhibernateSessionFactory
    {
        public ISessionFactory GetSessionFactory()
        {
           ISessionFactory fluentConfiguration = Fluently.Configure()
                                                  .Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionString")))
                                                  .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Map>().Conventions.Add(ForeignKey.EndsWith("Id")))
                                                  .ExposeConfiguration(cfg => cfg.SetProperty("adonet.batch_size", "20"))
                                                  //.ExposeConfiguration(BuidSchema)
                                                  .BuildSessionFactory();

            return fluentConfiguration;
        }

        private static void BuidSchema(NHibernate.Cfg.Configuration config)
        {
            new NHibernate.Tool.hbm2ddl.SchemaExport(config).Create(false, true);
        }
    }


public class NhibernateSessionFactoryProvider : Provider<ISessionFactory>
    {   
        protected override ISessionFactory CreateInstance(IContext context)
        {
            var sessionFactory = new NhibernateSessionFactory();
            return sessionFactory.GetSessionFactory();
        }
    }

  public class NhibernateModule : NinjectModule
    {
        public override void Load()
        {
            Bind<ISessionFactory>().ToProvider<NhibernateSessionFactoryProvider>().InSingletonScope();
            Bind<ISession>().ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope();
        }
    }

Изменить 3

Если я сделаю это

    public List<OrderHistory> GetOrderHistory(Guid Id)
    {
        using (ITransaction transaction = session.BeginTransaction())
        {       

 List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList();
        return orderHistories;
        }
    }

Я получу это предупреждение

Если я это сделаю

    public List<OrderHistory> GetOrderHistory(Guid Id)
    {
        using (ITransaction transaction = session.BeginTransaction())
        {       

 List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList().ConvertToLocalTime(timezoneId);
        transaction.Commit();
        return orderHistories;
        }
    }

Я могу избавиться от ошибок, но могу получить неожиданные результаты.

Например, когда я возвращаю OrderHistories обратно, я перебираю все из них и преобразовываю«Дата покупки» для пользователей местного времени.Это делается с помощью метода расширения, который я создал для своего списка.

После преобразования я установил его так, чтобы он переопределял «дату покупки» в объекте.Таким образом, мне не нужно создавать новый объект для одного изменения поля.

Теперь, если я выполняю это преобразование дат перед вызовом commit, nhibernate думает, что я обновил объект и должен его зафиксировать.

Итак, я вознаграждаю за этот вопрос.

  1. Как я могу создать свои методы, чтобы мне не нужно было оборачивать каждый метод в транзакции?Я уже использую ninject для своих сессий, так что, возможно, я смогу использовать это, хотя иногда, когда я вынужден делать несколько транзакций в одном запросе.

Так что я не знаю, иметь только одну транзакцию назапрос - это душа.

  1. как я могу убедиться, что объекты, которые я изменяю для временного использования, случайно не получают коммит?

  2. как мне выполнить отложенную загрузку, которую я использую в своем слое обслуживания.Я не хочу окружать мои ленивые загрузочные материалы транзакцией, поскольку они обычно используются в моем слое обслуживания.

Мне очень трудно найти примеры того, как это сделать, когдавы используете шаблон хранилища.В примерах все всегда написано в одной и той же транзакции, и я не хочу, чтобы транзакции были на моем сервисном уровне (это работа репо, а не моя бизнес-логика)

Ответы [ 4 ]

4 голосов
/ 24 мая 2011

Сообщество NHibernate рекомендует обернуть все в транзакции, независимо от того, что вы делаете.

Чтобы ответить на ваш второй вопрос, как правило, это зависит. Если это веб-приложение, вы должны посмотреть на шаблон сеанса для запроса. В большинстве базовых сценариев это означает, что для каждого HTTP-запроса вы создаете один сеанс, в котором сеанс (и транзакция) создается, когда запрос выполняется и фиксируется / удаляется в конце запроса. Я не говорю, что для вас это правильный путь, но это общий подход, который хорошо работает для большинства людей.

Существует множество примеров, показывающих, как это можно сделать. Определенно стоит потратить время на поиск и прочтение вещей.


РЕДАКТИРОВАТЬ : Пример того, как я выполняю сеанс / транзакцию для запроса:

У меня есть SessionModule, который загружает сессию из моего распознавателя зависимостей (это функция MVC3):

namespace My.Web
{
    public class SessionModule : IHttpModule {
        public void Init(HttpApplication context) {
            context.BeginRequest += context_BeginRequest;
            context.EndRequest += context_EndRequest;
        }

        void context_BeginRequest(object sender, EventArgs e) {
            var session = DependencyResolver.Current.GetService<ISession>();
            session.Transaction.Begin();
        }

        void context_EndRequest(object sender, EventArgs e) {
            var session = DependencyResolver.Current.GetService<ISession>();
            session.Transaction.Commit();
            session.Dispose(); 
        }

        public void Dispose() {}
    }
}

Вот так я регистрирую сессию (используя StructureMap):

new Container(x => {

    x.Scan(a => {
        a.AssembliesFromApplicationBaseDirectory();
        a.WithDefaultConventions();
    });

    x.For<ISessionFactory>().Singleton().Use(...);
    x.For<ISession>().HybridHttpOrThreadLocalScoped().Use(sf => sf.GetInstance<ISessionFactory>().OpenSession());
    x.For<StringArrayType>().Use<StringArrayType>();

});

Имейте в виду, что это то, над чем я экспериментировал и нашел, что он хорошо работает для сценариев, в которых я использовал NHibernate. У других людей могут быть разные мнения (которые, конечно, приветствуются).

2 голосов
/ 10 июня 2011

Вы передаете ISession в конструктор хранилища, и это хорошо.Но это единственное, что мне нравится в этом классе.

  • Сохранить только вызовы session.Save, так что в этом нет необходимости.
  • Похоже, что GetOrderHistory извлекает одну сущность по идентификатору, выследует использовать session.Get<OrderHistory>(id) для этого.При необходимости вы можете поместить результат в коллекцию.
  • Метод Commit не должен находиться в хранилище.

Чтобы отвечать на ваши вопросы напрямую ...

Как я могу создать свои методы, чтобы мне не пришлось заключать каждый метод в транзакцию?Я уже использую ninject для своих сессий, поэтому, возможно, я смогу использовать это несколько раз, хотя я вынужден выполнять несколько транзакций за один запрос.

Шаблон, который я рекомендую, приведен ниже.При этом используется ручное внедрение зависимостей, но вы можете использовать Ninject для разрешения ваших зависимостей.

List<OrderHistory> orderHistories;
var session = GetSession(); // Gets the active session for the request
var repository = new OrderHistory(Repository);
// new up more repositories as needed, they will all participate in the same transaction
using (var txn = session.BeginTransaction())
{
    // use try..catch block if desired
    orderHistories = repository.GetOrderHistories();
    txn.Commit();
}

Так что я не знаю, что только одна транзакция на запрос является душой.

Совершенно нормально иметь несколько транзакций в сеансе.Мне не нравится ждать, пока запрос не завершится, потому что уже слишком поздно, чтобы предоставить хороший отзыв пользователю.

как я могу убедиться, что объекты, которые я изменяю для временного использования, неслучайно получить коммит?

Единственный верный способ - использовать IStatelessSession.Менее надежные способы - исключить объект из сеанса или очистить сеанс.Но с NHibernate не рекомендуется изменять постоянные объекты.

как я могу иметь ленивую загрузку, которую я использую в своем сервисном слое.Я не хочу окружать мои ленивые загрузочные материалы транзакцией, поскольку они обычно используются в моем слое обслуживания.

Если вы используете сеанс для запроса, это не должно быть проблемой.Но вы правы, что ленивая загрузка может происходить вне транзакции.Я игнорирую эти предупреждения.Я полагаю, вы могли бы «дотронуться» до каждого необходимого дочернего объекта, чтобы ленивые загрузки были в транзакции, но я не беспокоюсь.

Я не хочу, чтобы транзакции были на моем уровне обслуживания (эторабота репо не моя бизнес логика)

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

2 голосов
/ 24 мая 2011

Ну, я думаю, вы могли бы установить уровень транзакции, соответствующий типу операций чтения, которые вы выполняете в своем приложении, но вопрос: действительно ли это необходимо делать в коде приложения?Я предполагаю, что нет, если только у вас нет варианта использования, который отличается от уровней транзакций по умолчанию, которые (n) hibernate будет применять в зависимости от конфигурации.

Возможно, вы можете установить уровни транзакций в вашей конфигурации nhibernate.

Или, может быть, настройки для профилировщика немного переусердствовали?Отсюда не могу сказать.

Но: вы пытались совершить транзакцию чтения?Не должно причинять вреда.

0 голосов
/ 09 июня 2011
  1. Рекомендуемый подход - единица работы (сеанс + транзакция) на запрос.Конечно, вы можете использовать NInject для управления жизненным циклом сеанса, недавно я написал в блоге о похожем подходе с использованием Castle Windsor.
  2. Вот 4 варианта:

    • Донне меняйте сущности временно
    • Используйте сеанс без сохранения состояния в таких случаях
    • Отключите объекты, когда вы собираетесь делать временное изменение
    • Откат транзакции

    Я бы пошел с первым.

  3. Вам не нужно беспокоиться о отложенной загрузке, если вы используете шаблон сеанса для запроса - он будет выполнен в том же запросе и автоматически обернут транзакцией.
...