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

Ser*_*gel 20 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; } // assigned
    public virtual string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

实例化并保存父母和子女:

child = new Child() { ID = 1, Name = "SomeName" };
parent = new Parent() { Children = new List() { child } };
session.Save(parent);
Run Code Online (Sandbox Code Playgroud)

这给了我:

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

我认为问题在于孩子的分配ID.由于它有一个id,NHibernate认为它以前已经保存过,但事实并非如此.

生成(修剪和重命名)的SQL是:

NHibernate: select child0_.ID as child1_1_, child0_.NAME as NAME1_, child0_.PARENT_ID as COMMAND7_1_, from CHILD child0_
NHibernate: select parent0_.PARENT_ID as parent1_10_
NHibernate: select parent0_.PARENT_ID as parent1_10_, parent0_.NAME as parent2_10_ from PARENT parent0_
NHibernate: UPDATE CHILD SET PARENT_ID = @p0 WHERE CHILD_ID = @p1;@p0 = 2, @p1 = 1
Run Code Online (Sandbox Code Playgroud)

映射文件:

<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>
Run Code Online (Sandbox Code Playgroud)

在搜索谷歌时,我发现版本标签可能是一个解决方案,但我没有一个持久字段用作版本.在这种情况下,如何保存(插入)具有指定ID及其父级的子级?

g .*_*g . 28

当从父级连接到子级时,NHibernate使用SaveOrUpdate方法.你是正确的NHMNnate需要一些方法来确定它是否应该执行插入或更新.它将查看未保存值的三个不同字段,以确定实体是否为新实体.

  1. ID
  2. 时间戳

使用指定的Id,您将需要Version或Timestamp字段以指示该实体是新的.

另一种方法是明确地对子项调用Save().

  • +1克!这有助于我找到解决Fluent NHibernate问题的方法.我的子实体正在调用更新而不是插入.要修复,我必须添加到映射的Id,例如`Id(x => x.Id).UnsavedValue(*defaultValue*);`where`*defaultValue*`匹配我的对象中的值,例如int:-1 ,Guid:Guid.Empty (3认同)

ano*_*ous 15

我不是100%确定你是否遇到了同样的问题,但是我的数据库是100%分配的id(呃)我必须创建一个拦截器来跟踪一个孩子是否被保留为级联上班.

代码是剪切/粘贴(这就是为什么它有愚蠢的名字......我起初并不理解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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

您添加到会话的拦截器:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

基本上这个想法是因为NHibernate不知道指定的id实体是否被持久化,所以你需要跟踪它.

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

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