NHibernate中的事务 - UPDATE然后INSERT。我究竟做错了什么?

时间:2010-09-10 12:00:14

标签: nhibernate fluent-nhibernate transactions

在这个示例控制台应用程序中,我想更新表中的一行,然后在同一个表中插入另一行。

表格就像这样

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()] ---&gt; System.Data.SqlClient.SqlException:无法在对象'dbo.Basket'中插入具有唯一索引'IX_Basket'的重复键行。

如果我刷新会话(注释掉的行)它可以工作,但为什么这是必要的?

我不想刷新会话并让Commit()处理它。

1 个答案:

答案 0 :(得分:5)

您无需保存/更新/ SaveOrUpdate会话中已有的任何实体。

您再次重复使用相同的ID。因此,请确保在之前刷新会话:

       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();
        }

这是因为您再次添加相同的唯一值。当然,您之前更改了现有值,但在刷新会话之前,这不会存储到数据库中。

会话在以下情况下刷新:

  • 你打电话给
  • 查询之前
  • (获取和加载除外)
  • on commit(除了使用自己的ADO连接)

当您调用“保存”或“更新”时,NH会对数据库执行更新或插入,这是一种常见的误解。不是这种情况。刷新会话时执行插入和更新。 (有一些例外情况,例如使用原生ID时。)