PavelGatilov wrote:
The initial dependencies are correct.
Thank you for pointing out the issues in the repro. I was not careful enough while writing it. However, you are right saying that Context usage and selecting correct e3 by the Value property does not fix it.
Indeed, though it's necessary, otherwise you end up with multiple objects with the same data (as e.g. e1 is referenced multiple times)
I added lines for every edge fetched again to the same graph and found that you don't fetch Ept from E. So you have to add the subpath to the EEntity prefetch
.Prefetch<EEntity>(pt => pt.Es).SubPath(f=>f.Prefetch<EptEntity>(p=>p.Ept))
if you want the reference to be present in memory (otherwise the reference isn't there. The FK field values of course are as they're part of the entity).
I guess the issue in Make changes is that it creates a cycle between 3 entities: e, te and re:
e depends on te and re, te depends on e, re depends on te. Therefore they are kind of equal. I am afraid I cannot remove this cycle. And the fact that Commit works fine if the first processed entity is pt, but breaks if starts from urr (depending on re via a EptEntity) makes me think it should actually work in all cases.
yes, and there's another cycle at line 90:
te.Obe = te;
here the instance te references itself. This can never work, as it can't persist itself.
the lines:
e.Obe = te;
e.Rbe = re;
these two create a cycle indeed, as e points to te, but te already pointed to e (line 45). The Rbe reference too creates a cycle (e-> re -> te -> e)
As it encounters a cycle, it skips the branch. This is expected, topological sort doesn't work on graphs with a cycle (it's a DAG algorithm). This is ok though, as graphs with a cycle can't be persisted in 1 go anyway. That the code does persist something when you start with another node is 'luck', as the cycle edges are ignored in such a way that it doesn't affect whole entities being cut off. The FK's still aren't persisted.
I am sorry for weird entity names. Though I am not sure if real names would help, unless you are very familiar with insurance business
The Russian equivalents likely wouldn't have helped
.
Ok, now that that's cleared up, how to fix this. The main issue is that the graph traverser does its work in 1 go: it sorts the graph and along the way it collects work. This fails when there's a cycle as you described in your second post, because work for the update queue is skipped as it's seen as work to be done before elements which are for the insert queue (te and re) which isn't true of course: e depends on te and re but not for an insert, for an update, which is always executed after the inserts (that is, by default, one can execute updates before inserts in a UoW, though that's always based on the queues calculated, it fails already before that).
in theory I think if the traverser walks the graph twice, once with a clean slate for the inserts, then again for the updates, starting with the entities in the insert queue as 'processed' and not any other entity, it could work: e then would be processed again (as it should, it's not in the insert queue) and ne and te are inserted before it, so fk syncs take place.
Of course except the te.Obe = te; line, that will never work.
This likely isn't doable in this version (v4.1) as it will break the DetermineActionQueues code, but I'll see what I can do. v4.2 is currently in development, so if I can postpone the real work to v4.2 and add a workaround in the UoW ConstructSaveProcessQueues it might help you a bit.
Perhaps there's an easier fix, I have to examine the code more closely, which I'll do tomorrow (friday).