【问题标题】:NHibernate trying to Insert an Existing Record for FK Mapping but only when int ID is 0NHibernate 尝试为 FK 映射插入现有记录,但仅在 int ID 为 0 时
【发布时间】:2013-07-25 16:39:35
【问题描述】:

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

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

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

这意味着:

  1. 打开新会话,从数据库中获取缓冲区消息,关闭会话
  2. 在我的应用中进行一些处理,将缓冲区消息的位置更新为 {ID=0, Name="TestLocation"}。
  3. 打开新会话,通过调用 Session.Load(0) 获取新位置,关闭会话。
  4. 将缓冲区消息的 BufferMessageLocation 更新为在步骤 3 中检索到的位置。
  5. 打开新会话,对缓冲区消息调用 Session.Merge(buffer),关闭会话。

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

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

  1. 为什么我只是在引用刚从数据库中获取的记录时插入了记录?

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

值得注意的是,这只发生在我将位置更新为 ID=0 时。我尝试将 Name="TestLocation" 记录的 ID 更改为 1、2、3、4 和 5,它们都可以工作。 ID 的几乎每个值都有效,除了 0。我什至尝试将该记录移动到 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 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>();
    }
}

流畅的映射

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

【问题讨论】:

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


    【解决方案1】:

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

    【讨论】:

    • 非常感谢!我正在拉扯我的头发试图弄清楚这里发生了什么。我会试试看。
    • 我注意到这只发生在实体与会话分离时。所以我假设只有在需要重新附加实体时才检查特殊身份值?谢谢。
    • 为什么不在 UnsavedValue 中传递 -1 ?
    • 避免魔法值。我使用了一个常量,所以两个地方都是一样的
    猜你喜欢
    • 1970-01-01
    • 2012-01-31
    • 2016-10-01
    • 2015-12-30
    • 2013-04-13
    • 2023-04-11
    • 1970-01-01
    • 1970-01-01
    • 2014-09-06
    相关资源
    最近更新 更多