如何在NHibernate中从子级创建级联时获取新的父ID?

时间:2015-01-29 16:46:09

标签: c# nhibernate fluent-nhibernate

我有一个父母子女关系,父母没有引用孩子 - 如下所示:

class Child
{
    int Id { get; set;
    Parent Parent { get; set; }
    // + other stuff
}

class Parent
{
    int Id { get; set; }
    // + other stuff
}

在数据库中,这只意味着Child表有一个ParentID列。此列可以为空 - 即可以Parent - 少Child

我使用Fluent惯例来映射域名,但我们可以覆盖此关系,以便我们可以同时更新Child并创建新的Parent时间:

public class ChildOverride : IAutoMappingOverride<Child>
{
    public void Override(AutoMapping<Child> mapping)
    {
        mapping.References(x => x.Parent).Cascade.All();
    }
}

当我们向现有Parent添加新的Child并立即需要读取该父级的ID(在交易中)时,问题就出现了,例如, :

existingChild.CreateParent(parameters);
session.Save(existingChild);
Debug.WriteLine(existingChild.Parent.Id);

只打印0而不是给我新父母的ID - 我假设Cascade.All()会......好吧,级联。这是错的吗?

如果我在访问Id之前提交了事务,一切都很好,或者如果我明确地保存了新的Parent也可以,例如。

// This works
existingChild.CreateParent(parameters);
session.Save(existingChild);
transaction.Commit();
Debug.WriteLine(existingChild.Parent.Id);

// This also works
existingChild.CreateParent(parameters);
session.Save(existingChild);
session.Save(existingChild.Parent);
Debug.WriteLine(existingChild.Parent.Id);

有什么方法可以更改我的覆盖,以便保存子对象将允许父ID立​​即可访问? (或者还有别的我做错了吗?)

2 个答案:

答案 0 :(得分:2)

好处是,一切正常您的映射是正确的。因为如果这个片段正在运行......

// This works
existingChild.CreateParent(parameters);
session.Save(existingChild);
transaction.Commit();

......这个概念也在发挥作用。为什么?什么都有关系?为什么Commit()有助于解决这个问题?

好吧,因为 ISession 抽象,所以它是虚拟持久性存储。在我们调用session.Save()时,执行任何SQL语句。它只保留所有信息(在使用该会话期间收集)并仅执行SQL WRITE语句

  • 如果绝对必须(更远的东西需要DB生成的ID)
  • 如果会话决定(如果允许...请参阅下面的会话模式自动)
  • 如果明确要求

但更好更精确的是引用doc:

9.6. Flush

  

ISession将不时执行将ADO.NET连接状态与内存中保存的对象状态同步所需的SQL语句。默认情况下,此过程刷新,发生在以下几点

...

  

除非明确Flush(),否则绝对无法保证Session何时执行ADO.NET调用,只保证执行它们的顺序。 然而,NHibernate确实保证ISession.Find(..)方法永远不会返回过时的数据; 也不会返回错误的数据。

     

可以更改默认行为,以便更频繁地进行刷新。 FlushMode 类定义了三种不同的模式:仅在提交时刷新(并且仅在使用NHibernate ITransaction API时),使用解释的例程自动刷新(仅在明确的NHibernate ITransaction中工作),或者除非明确调用Flush(),否则永远不会刷新最后一种模式对于长时间运行的工作单元很有用,其中ISession保持打开状态并长时间断开连接(参见第11.4节“乐观并发控制”)。

答案是肯定的。 FlushMode设置:

public enum FlushMode
{
    Unspecified = -1,
    Never = 0,
    Commit = 5,
    Auto = 10,
    Always = 20,
}

因此,如果FlushMode为Commit - 只有事务提交才会触发session.Flush()

但我们可以随时随地做到:

// This works
existingChild.CreateParent(parameters);
session.Save(existingChild);
session.Flush();

现在父级将拥有ID - 因为会话状态刚刚转换为SQL WRITE操作...

答案 1 :(得分:0)

您正在使用ID生成策略,该策略仅在对象访问数据库时生成ID。如果您希望在此之前拥有父ID,则需要使用assigned策略将ID生成带到您的手中。

这是一个很长的话题,我不想在此重复。您可以在这里阅读您需要做的事情: Don't Let Hibernate Steal Your Identity

在文章中,ID类型是UUID。您可以轻松地为intlong数据类型实现自定义ID生成器。