// 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; } }
// Builds a sample graph from http://en.wikipedia.org/wiki/Strongly_connected_components private static void SetupSampleGraph(SccGraph gr) { var a = gr.Add("a"); var b = gr.Add("b"); var c = gr.Add("c"); var d = gr.Add("d"); var e = gr.Add("e"); var f = gr.Add("f"); var g = gr.Add("g"); var h = gr.Add("h"); //links a.AddLink(b); b.AddLink(e, f, c); c.AddLink(d, g); d.AddLink(c, h); e.AddLink(a, f); f.AddLink(g); g.AddLink(f); h.AddLink(g, d); }