2010-09-10 50 views
1

在此示例控制台应用程序中,我想更新表中的一行,然后在同一个表中插入另一行。NHibernate中的事务 - UPDATE然后INSERT。我究竟做错了什么?

表是这样的

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。篮子] [SQL:INSERT INTO [Basket](UserId)VALUES(?);选择SCOPE_IDENTITY()] ---> System.Data.SqlClient.SqlException:不能在具有唯一索引'IX_Basket'的对象'dbo.Basket'中插入重复键行。

如果我冲洗会议(注释掉行)它的作品,但为什么这是必要的?

我不希望刷新我的会话并让Commit()处理它。

回答

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

这是因为您再次添加相同的独特价值。当然,你之前改变了现有的值,但是在刷新会话之前,这并没有存储到数据库中。

时,会被刷新:

  • 你调用flush
  • 查询之前(除获取和加载)上提交
  • (除非你用你自己的ADO连接)

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

+0

谢谢你的一个很好的答案 - 简洁和重点 – Rasmus 2010-09-13 06:41:23