如何在nhibernate中保存具有指定id的子项

如何在nhibernate中保存具有指定id的子项,nhibernate,Nhibernate,我有两门课: public class Parent { public virtual long? ID { get; set; } // native public virtual IList<Child> Children { get; set; } public virtual string Name { get; set; } } public class Child { public virtual long ID { get; set;

我有两门课:

public class Parent
{
    public virtual long? ID { get; set; } // native
    public virtual IList<Child> Children { get; set; }
    public virtual string Name { get; set; }
}

public class Child
{
    public virtual long ID { get; set; } // assigned
    public virtual string Name { get; set; }
}
这给了我:

NHibernate.StaleStateException:意外的行计数:0;预期:1

我想问题出在孩子身上指定的id。由于它有一个id,NHibernate认为它以前保存过,但事实并非如此

生成的(修剪并重命名)SQL为:

映射文件:

<class name="MyNamespace.Child" table="CHILD">
  <id name="ID" column="CHILD_ID" type="System.Int64">
    <generator class="assigned"></generator>
  </id>
  <property name="Name" column="NAME"></property>
</class>

<class name="MyNamespace.Parent" table="PARENT">
  <id name="ID" column="PARENT_ID" type="System.Int64">
    <generator class="native"></generator>
  </id>
  <property name="Name" column="NAME"></property>
  <bag name="Children">
    <key column="PARENT_ID"></key>
    <one-to-many class="MyNamespace.Child"></one-to-many>
  </bag>
</class>


在搜索google时,我找到了关于版本标签的信息,这可能是一个解决方案,但我没有一个持久字段用作版本。在这种情况下,如何保存(插入)具有指定id的子级及其父级?

当从父级级联到子级时,NHibernate使用SaveOrUpdate方法。您是对的,NHibernate需要某种方法来确定它应该执行插入还是更新。它将在三个不同的字段中查找未保存的值,以确定实体是否是新的

  • 身份证
  • 版本
  • 时间戳
  • 对于分配的Id,您需要一个版本或时间戳字段来指示实体是新的


    另一种方法是显式地对子对象调用Save()。

    我不能100%确定这是否与您遇到的问题相同,但我的数据库是100%分配的id(ugh),我必须创建一个拦截器,跟踪子对象是否被持久化,以便级联工作

    代码是剪切/粘贴的(这就是为什么它有愚蠢的名字…一开始我没有100%理解!),最初我90%的代码来自在线文档(我现在无法通过谷歌找到…对不起):

    放置在具有要级联的指定ID的对象上的基类:

    public class Persistent
    {
        private bool _saved = false;
    
        public virtual void OnSave()
        {
            _saved = true;
        }
    
        public virtual void OnLoad()
        {
            _saved = true;
        }
    
        public virtual bool IsSaved
        {
            get { return _saved; }
        }
    }
    
    添加到会话的侦听器:

    public class TrackingNumberInterceptor : EmptyInterceptor
    {
        public override bool? IsTransient(object entity)
        {
            if (entity is Persistent)
            {
                return !((Persistent)entity).IsSaved;
            }
            else
            {
                return null;
            }
        }
    
        public override bool OnLoad(object entity, object id, object[] state, string[] propertyNames, IType[] types)
        {
            if (entity is Persistent) ((Persistent)entity).OnLoad();
            return false;
        }
    
        public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types)
        {
            if (entity is Persistent) ((Persistent)entity).OnSave();
            return false;
        }
    }
    
    基本上,这个想法是因为NHibernate不知道分配的id实体是否被持久化,所以您需要跟踪它

    默认情况下,对象以false的persistend(_saved)开头。当NHibernate加载或保存实体时,触发器将对象持久化(_saved)标志设置为true


    因此,对于未持久化的新项目,它从false开始并保持false,因为NHibernate从未保存或加载它。当NHibernate检查子项是否是瞬态时,触发器会响应它是瞬态的,并且会发生一次保存,将该子项标记为持久化。现在,任何将来的使用都需要一个加载,它会再次将其标记为持久化。

    调用会话。SaveOrUpdate(childObject)应该可以解决这个问题。

    您可以发布您的关系映射吗?好的,我现在已经包括了它们。不确定这是否会解决问题,或者与“分配的”id无关,我从未使用过它们。但是试着在你的包属性上设置inverse=“true”。这个特殊的方法为我们解决了很多问题。仅供参考-找到此代码的文档直接来自NHibernate文档。该文档也解决了我的问题。非常直截了当且易于应用。这对我来说也很有效,但我必须在IsTransient:return中删除这行的否定词!((持久)实体)。已保存。在此之前,我看到了空引用问题(通过NullGuard)。+1g!这帮助我找到了解决Fluent NHibernate问题的方法。我的子实体正在调用update而不是insert。为了解决这个问题,我必须添加到映射的Id中,例如
    Id(x=>x.Id)
    其中
    *defaultValue*
    与我的对象中的值匹配,例如int:-1,Guid:Guid.Empty+1,以最简洁的方式解释nhibernate如何确定实体在web上是新的还是分离的。我遇到了类似的问题,但不是父子问题。它只是一个具有指定Id(int)的实体,我需要插入它。我没有版本和时间戳列,但我想我的案例不需要它们。当我尝试插入实体时(只需调用Save()方法),我会得到相同的StaleStateException。NHProf显示已发送UPDATE语句(显然是错误的)。你知道我做错了什么,为什么NHibernate还试图更新吗?明白了!调用Save方法时无法设置id(必须为0),因此您必须使用提供id的重载。显然,Save并不总是一个insert…:o(
    public class Persistent
    {
        private bool _saved = false;
    
        public virtual void OnSave()
        {
            _saved = true;
        }
    
        public virtual void OnLoad()
        {
            _saved = true;
        }
    
        public virtual bool IsSaved
        {
            get { return _saved; }
        }
    }
    
    public class TrackingNumberInterceptor : EmptyInterceptor
    {
        public override bool? IsTransient(object entity)
        {
            if (entity is Persistent)
            {
                return !((Persistent)entity).IsSaved;
            }
            else
            {
                return null;
            }
        }
    
        public override bool OnLoad(object entity, object id, object[] state, string[] propertyNames, IType[] types)
        {
            if (entity is Persistent) ((Persistent)entity).OnLoad();
            return false;
        }
    
        public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types)
        {
            if (entity is Persistent) ((Persistent)entity).OnSave();
            return false;
        }
    }