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

本文关键字:int 0时 ID 记录 插入 FK 映射 NHibernate | 更新日期: 2023-09-27 18:24:52

我正在开发一个消息传递系统,其中有一个名为Buffer的对象,当消息在系统中传播时,它存储传入消息及其位置(用于消息跟踪)。我遇到了一些问题,这些问题表明我的应用程序或NHibernate存在根本问题。我正在使用Fluent NHibernate进行映射。

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

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

这意味着:

  1. 打开新会话,从DB获取缓冲区消息,关闭会话
  2. 在我的应用程序中进行一些处理,将缓冲区消息的位置更新为{ID=0,Name="TestLocation"}
  3. 打开新会话,通过调用session获取新位置。加载<BufferMessageLocation>(0),关闭会话
  4. 将缓冲区消息的BufferMessageLocation更新为步骤3中检索到的位置
  5. 打开新会话,调用会话。对缓冲区消息进行合并(缓冲区),关闭会话

从我读到的关于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)]

因此,每当我将缓冲区的位置更新为该值时,它似乎正在尝试插入一个新的BufferMessageLocation,名称="TestLocation"(已存在),ID=null。这种情况有两个错误:

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

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

值得注意的是,只有当我将位置更新为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的问题或我做错了什么,我将感谢您的反馈。下面是我的全部代码。

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

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

应用程序代码(请忽略类/对象名称上的"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类(用于此目的的域类):

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

通量映射

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

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

Id==0被特殊处理为"Id未设置"将未设置的值更改为不同的值,并在构造函数中初始化它

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

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