I'm in a unique position whereby I am using NHibernate in a winforms application that exposes its domain objects through a kind of application automation API (think Word.Application, for consumption by macros, scripts and add-ins, etc).
Since my database schema is quite complex, I have exposed a transaction API so that scripters don't need to worry so much about nasty foreign key exceptions if they accidentally attach entities in the wrong order (since all the entities will be batched).
My problem is that when an entity has multiple parents who themselves have parents is inserted, the internal batch insertion queue is not in the appropriate order. For example, the insertion of [Child] in the schema below:
Code:
LeftGrandParent -< LeftParent -< Child >- RightParent >- RightGrandParent
Could result in the following insertion queue:
LeftGrandParent
RightGrandParent
Child
LeftParent
RightParent
As you can see, the insertion of [Child] will fail since the [LeftParent] and [RightParent] foreign entities wont exist in the database yet.
The usual solution to this is to simply commit the parents before the child in a separate transaction. But as I said, I'm trying to hide such implementation details from my potential scripters.
To temporarily get around this problem, I have made a small change to the SessionImpl.ExecuteAll() method whereby it loops through the insertions over and over (ignoring any exceptions) until either all of them work or none of them work (just like the YAST2 installer). If it gets to a stage where none of the executions succeed, it raises the first exception encountered.
Is there a cleaner way to go about this?
If not, is this useful enough to warrant a feature request?
Does it look like the the simple patch below could break anything (all the unit tests still succeed)?
Cheers,
Nathan
Code:
Index: SessionImpl.cs
===================================================================
--- SessionImpl.cs (revision 2568)
+++ SessionImpl.cs (working copy)
@@ -3238,12 +3238,44 @@
private void ExecuteAll(IList list)
{
- foreach (IExecutable e in list)
- {
- Execute(e);
- }
- list.Clear();
+ int count = list.Count;
+ while (count > 0)
+ {
+ bool allFailed = true;
+ Exception firstException = null;
+
+ for (int i = 0; i < count; i++)
+ {
+ try
+ {
+ Execute((IExecutable)list[i]);
+ list.RemoveAt(i);
+ i--;
+ count--;
+ allFailed = false;
+ }
+ catch (Exception ex)
+ {
+ if (firstException == null)
+ {
+ firstException = ex;
+ }
+ }
+ }
+
+ if (allFailed)
+ {
+ throw firstException;
+ }
+ }
+
if (batcher != null)
{
batcher.ExecuteBatch();