[英]How do I get a new parent ID when cascade creating from a child in NHibernate?
I have a parent child relationship where the parent doesn't have a reference to the children - something like this : 我有一个父子关系,父母没有对孩子的引用-像这样:
class Child
{
int Id { get; set;
Parent Parent { get; set; }
// + other stuff
}
class Parent
{
int Id { get; set; }
// + other stuff
}
In the database this just means that the Child
table has a ParentID
column. 在数据库中,这仅意味着
Child
表具有ParentID
列。 This column can be null - ie it's possible to have a Parent
-less Child
. 该列可以为空-即可以有一个没有
Parent
Child
。
I'm using Fluent conventions to map the domain, but there's an override for this relationship so that we can update a Child
and create a new Parent
at the same time : 我正在使用Fluent约定来映射域,但是对此关系有一个替代,以便我们可以同时更新
Child
并创建新的Parent
:
public class ChildOverride : IAutoMappingOverride<Child>
{
public void Override(AutoMapping<Child> mapping)
{
mapping.References(x => x.Parent).Cascade.All();
}
}
The problem arises when we add a new Parent
to an existing Child
and immediately need to read the ID of that Parent (while in a transaction), eg : 当我们向现有
Child
添加新的Parent
并且立即需要(在交易中)读取该父代的ID时,就会出现问题,例如:
existingChild.CreateParent(parameters);
session.Save(existingChild);
Debug.WriteLine(existingChild.Parent.Id);
Which just prints 0
rather than giving me the Id of the new Parent - I had assumed Cascade.All()
would...well, cascade. 它只是打印
0
而不是给我新的Parent的Cascade.All()
我假设Cascade.All()
会...好吧,级联。 Is that wrong? 错了吗
If I commit the transaction before accessing the Id, everything is fine, or if I save the new Parent explicitly that works too, eg 如果我在访问ID之前提交了事务,那么一切都很好,或者如果我也明确保存了新父类也可以工作,例如
// 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);
Is there any way I can change my override so that saving the child object will allow the parent id to be immediately accessible? 有什么方法可以更改覆盖,以便保存子对象将允许立即访问父ID? (Or is there something else I'm doing wrong?)
(或者我做错了什么吗?)
The good is, that all is working properly . 这样做的好处是, 所有人都在正常工作 。 And your mapping is correct .
而且您的映射是正确的 。 Because if this snippet is working...
因为如果该代码段有效...
// This works
existingChild.CreateParent(parameters);
session.Save(existingChild);
transaction.Commit();
... the concept is working as well. ...这个概念也行得通。 Why?
为什么? What is all about?
到底是什么? Why
Commit()
helped to solve that? 为什么
Commit()
可以解决这个问题?
Well, because ISession
is an abstraction , it is the virtual persistence storage. 好吧,因为
ISession
是一个抽象 ,所以它是虚拟的持久性存储。 It does not execute any SQL Statements in the moment we call session.Save()
. 在我们调用
session.Save()
的那一刻,它不执行任何SQL语句。 It just keeps all information (gathered during working with that session) and executing the SQL WRITE Statements only 它仅保留所有信息(在该会话期间收集)并仅执行SQL WRITE语句
But better and more precise will be to cite doc: 但是更好更精确的是引用doc:
From time to time the ISession will execute the SQL statements needed to synchronize the ADO.NET connection's state with the state of objects held in memory.
ISession会不时执行将ADO.NET连接状态与内存中保存的对象状态同步所需的SQL语句。 This process, flush, occurs by default at the following points
默认情况下,此过程在以下几点进行刷新
... ...
Except when you explicity
Flush()
, there are absolutely no guarantees about when the Session executes the ADO.NET calls , only the order in which they are executed.除了显式地使用
Flush()
, 绝对不能保证Session何时执行ADO.NET调用 ,只能保证它们执行的顺序。 However, NHibernate does guarantee that the ISession.Find(..) methods will never return stale data;但是,NHibernate确实保证了ISession.Find(..)方法将永远不会返回过时的数据。 nor will they return the wrong data.
他们也不会返回错误的数据。
It is possible to change the default behavior so that flush occurs less frequently.
可以更改默认行为,以便减少刷新的频率。 The FlushMode class defines three different modes: only flush at commit time (and only when the NHibernate ITransaction API is used), flush automatically using the explained routine (will only work inside an explicit NHibernate ITransaction), or never flush unless Flush() is called explicitly.
FlushMode类定义了三种不同的模式:仅在提交时进行刷新(并且仅在使用NHibernate ITransaction API时);使用说明的例程自动进行刷新(仅在显式的NHibernate ITransaction中起作用);或者除非Flush()为显式调用。 The last mode is useful for long running units of work, where an ISession is kept open and disconnected for a long time (see Section 11.4, “Optimistic concurrency control”).
最后一种模式对于长时间运行的工作单元很有用,在这种情况下,ISession长时间保持打开和断开状态(请参见第11.4节“乐观的并发控制”)。
And there is the answer. 并且有答案。 The
FlushMode
setting: FlushMode
设置:
public enum FlushMode
{
Unspecified = -1,
Never = 0,
Commit = 5,
Auto = 10,
Always = 20,
}
So, if the FlushMode is Commit
- only transaction commit triggers the session.Flush()
因此,如果FlushMode为
Commit
只有事务提交才触发session.Flush()
But we can do it any time ourselves: 但是我们可以随时自己做:
// This works
existingChild.CreateParent(parameters);
session.Save(existingChild);
session.Flush();
And now parent will have the ID - becuase session state was just converted into SQL WRITE operations... 现在父级将具有ID-因为会话状态刚刚转换为SQL WRITE操作...
You are using a ID generation strategy that only generates the ID when the object hits the DB. 您正在使用ID生成策略,该策略仅在对象命中数据库时才生成ID。 If you want to have parent ID before that, you'll need to take the ID generation into your hand, using
assigned
strategy. 如果您希望在此之前拥有父ID,则需要使用
assigned
策略将ID生成交给您。
It's a long topic that I don't want to repeat here. 这个话题很长,我不想在这里重复。 You can read about what you need to do here: Don't Let Hibernate Steal Your Identity
您可以在这里阅读有关您需要做的事情: 不要让休眠状态窃取您的身份
In the article, the ID type is UUID. 在本文中,ID类型为UUID。 You can easily implement a custom ID generator for
int
or long
data types. 您可以轻松地为
int
或long
数据类型实现自定义ID生成器。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.