NHibernate尝试为FK映射插入现有记录,但仅当int ID为0时

时间:2013-07-25 16:39:36

标签: c# .net nhibernate fluent-nhibernate fluent

我正在开发一个消息系统,其中有一个名为Buffer的对象,它在消息通过系统时存储传入消息及其位置(用于消息跟踪)。我遇到了一些问题,这些问题指出了我的应用程序或NHibernate的根本错误。我正在使用Fluent NHibernate作为我的映射。

我有两个对象:缓冲区 BufferMessageLocation 。 Buffer是数据库中的一个表,每个记录代表一个缓冲消息(由代码中的Buffer类的实例表示)。 BufferMessageLocation本质上是一个查找值,可以有5个不同的值:0-4(每个值代表一个位置)。每个Buffer对象都包含一个BufferMessageLocation。 BufferMessageLocation在数据库中表示为Table [int ID,string Name]。

我必须做一些分离的实体处理,因为我的应用程序中缓冲消息的生命周期将比NHibernate会话的生命周期长。

这意味着:

  1. 打开新会话,从数据库中获取缓冲消息,关闭会话
  2. 在我的应用中进行一些处理,将缓冲区消息的位置更新为,例如{ID = 0,Name =“TestLocation”}。
  3. 打开新会话,通过调用Session.Load<获取新位置。 BufferMessageLocation>(0),关闭会话。
  4. 将缓冲区消息的BufferMessageLocation更新为在步骤3中检索的位置。
  5. 打开新会话,在缓冲区消息上调用Session.Merge(缓冲区),关闭会话。
  6. 从我所读到的关于NHibernate的所有内容中,这应该可行。但是,出于某种原因,NHibernate试图将新记录插入BufferMessageLocation,而不是仅仅更新Buffer记录。

    以下是错误消息:

    could not insert: [AutomationBase.RepositoryNS.DataAccess.NHMG.Fluent.DTO.DBBufferMessageLocation][SQL: INSERT INTO tblBufferMessageLocation (Name) VALUES (?); select SCOPE_IDENTITY()]    
    

    InnerException是:

    Cannot insert the value NULL into column 'ID', table 'AutomationNew.dbo.tblBufferMessageLocation'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated.
    

    生成的抛出此错误的SQL是:

    NHibernate: INSERT INTO tblBufferMessageLocation (Name) VALUES (@p0); select SCOPE_IDENTITY();@p0 = 'TestLocation' [Type: String (4000)]
    

    因此,当我将缓冲区的位置更新为该值时,看起来它正在尝试插入名为=“TestLocation”(已存在)的新BufferMessageLocation和ID = null。这种情况有两个问题:

    1. 当我只是引用我刚从数据库获取的记录时,为什么要插入 new 记录?

    2. 为什么在插入新记录时没有为BufferMessageLocation的ID提供值?

    3. 值得注意的是,只有当我将位置更新为ID = 0 时才会发生这种情况。我已经尝试将Name =“TestLocation”记录的ID更改为1,2,3,4和5,它们都可以正常工作。除了0之外,ID的每个值都可以工作。我甚至尝试将该记录移动到ID = 5,插入ID = 0的新BufferMessageLocation记录并将缓冲区消息的位置更新为此新记录,但问题仍然存在。所以似乎ID = 0会导致NHibernate的记录检测机制出现一些问题,因为所有其他的int ID值都有效。

      需要注意的另一个重要事项是,只有在我使用多个会话时才会发生这种情况。如果我在一个会话中完成所有操作,那么一切正常。我的猜测是,当调用合并分离实体时,Session.Merge无法检测到已经存在ID为0的BufferMessageLocation。

      如果有人知道为什么会发生这种情况和/或如果这是NHibernate的问题或我做错了什么,我会很感激反馈。我的全部代码如下。

      很抱歉这么长的帖子,但这是一个非常具体的问题,所以我不得不详细介绍。

      =============================================== ==========================

      APPLICATION CODE (请忽略类/对象名称上的“DB”前缀,它们用于分层):

      class Program
      {
          static void Main(string[] args)
          {
              DBBuffer dbBuffer;
              DBInstruction instruction;
              DBBufferMessageLocation bufferMessageLocation;
      
              using (ISession session = FNHHelper.OpenSession())
              {
                  using (ITransaction tran = session.BeginTransaction())
                  {
                      dbBuffer = session.QueryOver<DBBuffer>().List().First();
                      instruction = session.Load<DBInstruction>(2);
                      bufferMessageLocation = session.Load<DBBufferMessageLocation>(0);
                      tran.Commit();
                  }
              }
      
              //do application processing
      
              dbBuffer.BufferMessageLocation = bufferMessageLocation;
      
              //required attributes
              dbBuffer.Instruction = instruction;
              dbBuffer.Processed = false;
              dbBuffer.FromProducer = false;
      
              using (ISession session = FNHHelper.OpenSession())
              {
                  using (ITransaction tran = session.BeginTransaction())
                  {
                      session.Merge(dbBuffer); <======== This is where the error is thrown
                      tran.Commit();
                  }
              }
          }
      }
      

      DTO CLASSES (用于此目的的域类):

      public partial class DBBuffer : IRepositoryEntity 
      {
          public System.Guid ID { get; set; }
          public DBLog Log { get; set; }
          public DBError Error { get; set; }
          public DBInstruction Instruction { get; set; }
          public DBBufferMessageLocation BufferMessageLocation { get; set; }
          public DateTime BufferTime { get; set; }
          public string ClientIP { get; set; }
          public string Description { get; set; }
          public bool Processed { get; set; }
          public bool FromProducer { get; set; }
      }
      
      public partial class DBBufferMessageLocation : IRepositoryLookup 
      {
          public int ID { get; set; }
          public string Name { get; set; }
          public IList<DBBuffer> Buffers { get; set; }
      
          public DBBufferMessageLocation()
          {
              Buffers = new List<DBBuffer>();
          }
      }
      

      FLUENT MAPPINGS

      public partial class DBBufferMap : ClassMap<DBBuffer> {
      
          public DBBufferMap() {
              Table("tblBuffer");
              Id(x => x.ID).GeneratedBy.Assigned().Column("ID");
              References(x => x.Log).Column("LogID").Cascade.All();
              References(x => x.Error).Column("ErrorID").Cascade.All();
              References(x => x.Instruction).Column("InstructionID").Cascade.All();
              References(x => x.BufferMessageLocation).Column("BufferMessageLocationID").Cascade.All();
              Map(x => x.BufferTime).Column("BufferTime").Not.Nullable();
              Map(x => x.ClientIP).Column("ClientIP");
              Map(x => x.Description).Column("Description");
              Map(x => x.Processed).Column("Processed").Not.Nullable();
              Map(x => x.FromProducer).Column("FromProducer").Not.Nullable();
          }
      }
      
      public partial class DBBufferMessageLocationMap : ClassMap<DBBufferMessageLocation> {
      
          public DBBufferMessageLocationMap() {
              Table("tblBufferMessageLocation");
              Id(x => x.ID).GeneratedBy.Identity().Column("ID");
              Map(x => x.Name).Column("Name").Not.Nullable();
              HasMany(x => x.Buffers).KeyColumn("BufferMessageLocationID");
          }
      }
      

1 个答案:

答案 0 :(得分:2)

Id == 0被视为特殊为“Id not set”将未设置值更改为不同的值并在构造函数中初始化

public DBBufferMessageLocationMap()
{
    Id(x => x.ID, "ID").GeneratedBy.Identity().UnsavedValue(DBBufferMessageLocation.UNSET_ID);
}


public const int UNSET_ID = -1;
public DBBufferMessageLocation()
{
    Id = UNSET_ID;
}