// Note about special case: members with CascadeDelete attribute. // Demo case setup. 3 entities, IBook, IAuthor, and IBookAuthor as link table; IBookAuthor references IBook with CascadeDelete, // and references IAuthor without cascade. // Because of CascadeDelete, when we delete IBook and IBookAuthor in one operation, the order of IBook vs IBookAuthor does not matter: // even if IBook comes before IBookAuthor, delete will succeed because of cascade delete of IBookAuthor. // The problem case is when we are deleting IBook and IAuthor, without explicitly deleting IBookAuthor. // In this case IAuthor should be deleted after IBook - otherwise still existing IBookAuthor record // would prevent it from deleting. As there's no explicit IBookAuthor in delete set, and there's // no FK links between IAuthor and IBook - then they may come to delete in any order, and trans might fail. // The solution is to introduce an extra direct link between IBook and IAuthor in abstract SCC node tree. // This extra link will ensure proper topological ordering of IBook and IAuthor. // Note that we still need to add link between IBookAuthor and IBook - for proper ordering of inserts. private void ComputeTopologicalIndexes() { // Run SCC algorithm var g = new SccGraph(); //Perform SCC analysis. foreach (var ent in Model.Entities) { ent.SccVertex = g.Add(ent); } //setup links foreach (var ent in Model.Entities) { var cascadeMembers = new List <EntityMemberInfo>(); var nonCascadeMembers = new List <EntityMemberInfo>(); foreach (var member in ent.RefMembers) { var targetEnt = member.ReferenceInfo.ToKey.Entity; ent.SccVertex.AddLink(targetEnt.SccVertex); if (member.Flags.IsSet(EntityMemberFlags.CascadeDelete)) { cascadeMembers.Add(member); } else { nonCascadeMembers.Add(member); } }//foreach member //For all cascade member (IBookAuthor.Author) targets add direct links to all non-cascade member targets // (from IBook to IAuthor) foreach (var cascMember in cascadeMembers) { var cascTarget = cascMember.ReferenceInfo.ToKey.Entity; foreach (var nonCascMember in nonCascadeMembers) { var nonCascTarget = nonCascMember.ReferenceInfo.ToKey.Entity; cascTarget.SccVertex.AddLink(nonCascTarget.SccVertex); } } //foreach cascMember } //foreach ent //Build SCC var sccCount = g.BuildScc(); //read scc index and clear vertex fields foreach (var ent in Model.Entities) { var v = ent.SccVertex; ent.TopologicalIndex = v.SccIndex; if (v.NonTrivialGroup) { ent.Flags |= EntityFlags.TopologicalGroupNonTrivial; } ent.SccVertex = null; } }