Транзакции в NHibernate - ОБНОВЛЕНИЕ, а затем ВСТАВИТЬ.Что я делаю неправильно? - PullRequest
1 голос
/ 10 сентября 2010

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

Таблица выглядит так

CREATE TABLE [dbo].[Basket2](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [UserId] [int] NULL
) ON [PRIMARY]


CREATE UNIQUE NONCLUSTERED INDEX [IX_Basket] ON [dbo].[Basket2] 
(
    [UserId] ASC
)

Таким образом, в основном пользователь не может иметь 2 корзины.

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

Следующий код представляет собой пример приложения, которое имитирует поток - и дает сбой

private static void Main(string[] args)
    {
        ISessionFactory sessionFactory = CreateSessionFactory();

        int userId = new Random().Next();
        int basketId;
        using (var session = sessionFactory.OpenSession())
        {
            using (var tx = session.BeginTransaction(IsolationLevel.ReadUncommitted))
            {
                var newBasket = new Basket {UserId = userId};

                basketId = (int) session.Save(newBasket);
                tx.Commit();
            }

            using (var tx = session.BeginTransaction(IsolationLevel.ReadUncommitted))
            {
                var basket = session.Get<Basket>(basketId);
                basket.UserId = basket.Id*-1;
                session.Save(basket);

                // comment in this line to make it work:
                //session.Flush();

                var newBasket = new Basket {UserId = userId};
                session.Save(newBasket);
                tx.Commit();
            }
        }
    }

Ошибка:

Необработанное исключение: NHibernate.Exceptions.GenericADOException: невозможно вставить: [ConsoleApplication1.Basket] [SQL: INSERT INTO [Basket] (UserId) VALUES (?); выберите SCOPE_IDENTITY ()] ---> System.Data.SqlClient.SqlException: Невозможно вставить повторяющуюся строку ключа в объект 'dbo.Basket' с уникальным индексом 'IX_Basket'.

Если я сбрасываю сессию (закомментированные строки), это работает, но зачем это нужно?

Я бы предпочел не сбрасывать мой сеанс и не позволять Commit () его обрабатывать.

1 Ответ

5 голосов
/ 10 сентября 2010

Вам не нужно сохранять / обновлять / SaveOrUpdate любые объекты, которые уже находятся в сеансе.

Но вы снова используете тот же идентификатор снова.Поэтому убедитесь, что сеанс сбрасывается до:

       using (var tx = session.BeginTransaction(IsolationLevel.ReadUncommitted))
        {
            var basket = session.Get<Basket>(basketId);
            basket.UserId = basket.Id*-1;

            // no save
            //session.Save(basket);

            // flush change on unique field
            session.Flush();

            var newBasket = new Basket {UserId = userId};

            // save new item which is not in the session yet
            session.Save(newBasket);
            tx.Commit();
        }

Это потому, что вы снова добавляете то же уникальное значение.Конечно, вы изменяете существующее значение раньше, но оно не сохраняется в базе данных до сброса сеанса.

Сеанс сбрасывается, когда:

  • вы вызываете flush
  • перед запросами (кроме Get и Load)
  • при фиксации (кроме случаев, когда вы используете собственное соединение ADO)

Распространенным заблуждением является то, что NH выполняет обновление или вставку вбаза данных, когда вы звоните Сохранить или Обновить.Это не вариант.Вставка и обновление выполняются при очистке сеанса.(Есть некоторые исключения на этот счет, например, при использовании собственных идентификаторов.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...