diff --git a/src/NHibernate/Loader/JoinWalker.cs b/src/NHibernate/Loader/JoinWalker.cs index f15c220d6dc..5e1398f4607 100644 --- a/src/NHibernate/Loader/JoinWalker.cs +++ b/src/NHibernate/Loader/JoinWalker.cs @@ -115,14 +115,14 @@ protected virtual bool IsTooManyCollections get { return false; } } + //Since v5.3 + [Obsolete("This class is not used and will be removed in a future version.")] public class DependentAlias { public string Alias { get; set; } public string[] DependsOn { get; set; } } - readonly List _dependentAliases = new List(); - protected JoinWalker(ISessionFactoryImplementor factory, IDictionary enabledFilters) { this.factory = factory; @@ -181,7 +181,7 @@ private void AddAssociationToJoinTree(IAssociationType type, string[] aliasedLhs enabledFilters, GetSelectMode(path)); assoc.ValidateJoin(path); - AddAssociation(subalias, assoc); + AddAssociation(assoc); int nextDepth = currentDepth + 1; @@ -204,10 +204,25 @@ protected virtual SelectMode GetSelectMode(string path) return SelectMode.Undefined; } - private static int[] GetTopologicalSortOrder(List fields) + private struct DependentAlias2 + { + public DependentAlias2(string alias, ICollection dependsOn) + { + Alias = alias; + DependsOn = dependsOn; + } + + public string Alias { get; } + public ICollection DependsOn { get; } + } + + /// + /// Returns list of indexes in sorted order + /// + private static int[] GetTopologicalSortOrder(IList fields) { TopologicalSorter g = new TopologicalSorter(fields.Count); - Dictionary indexes = new Dictionary(StringComparer.OrdinalIgnoreCase); + Dictionary indexes = new Dictionary(fields.Count, StringComparer.OrdinalIgnoreCase); // add vertices for (int i = 0; i < fields.Count; i++) @@ -218,14 +233,12 @@ private static int[] GetTopologicalSortOrder(List fields) // add edges for (int i = 0; i < fields.Count; i++) { - var dependentAlias = fields[i]; - if (dependentAlias.DependsOn != null) + var dependentFields = fields[i].DependsOn; + if (dependentFields != null) { - for (int j = 0; j < dependentAlias.DependsOn.Length; j++) + foreach (var dependentField in dependentFields) { - var dependentField = dependentAlias.DependsOn[j]; - int end; - if (indexes.TryGetValue(dependentField, out end)) + if (indexes.TryGetValue(dependentField, out var end)) { g.AddEdge(i, end); } @@ -236,31 +249,40 @@ private static int[] GetTopologicalSortOrder(List fields) return g.Sort(); } - /// - /// Adds an association and extracts the aliases the association's 'with clause' is dependent on - /// - private void AddAssociation(string subalias, OuterJoinableAssociation association) + private static List GetDependentAliases(IList associations) { - var dependentAlias = new DependentAlias + var dependentAliases = new List(associations.Count); + foreach (var association in associations) { - Alias = subalias, - }; - _dependentAliases.Add(dependentAlias); + dependentAliases.Add(new DependentAlias2(association.RHSAlias, GetDependsOn(association))); + } - var on = association.On.ToString(); - if (!string.IsNullOrEmpty(on)) + return dependentAliases; + } + + private static HashSet GetDependsOn(OuterJoinableAssociation association) + { + if (SqlStringHelper.IsEmpty(association.On)) + return null; + + var dependencies = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (Match match in aliasRegex.Matches(association.On.ToString())) { - var dependencies = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (Match match in aliasRegex.Matches(on)) - { - string alias = match.Value; - if (string.Equals(alias, subalias, StringComparison.OrdinalIgnoreCase)) - continue; - dependencies.Add(alias); - } - dependentAlias.DependsOn = dependencies.ToArray(); + string alias = match.Value; + if (string.Equals(alias, association.RHSAlias, StringComparison.OrdinalIgnoreCase)) + continue; + + dependencies.Add(alias); } + return dependencies; + } + + /// + /// Adds an association + /// + private void AddAssociation(OuterJoinableAssociation association) + { associations.Add(association); } @@ -360,7 +382,7 @@ internal void AddExplicitEntityJoinAssociation( Factory, enabledFilters, GetSelectMode(path)); - AddAssociation(tableAlias, assoc); + AddAssociation(assoc); } private void WalkEntityAssociationTree(IAssociationType associationType, IOuterJoinLoadable persister, @@ -799,16 +821,9 @@ protected SqlString MergeOrderings(string ass, string orderBy) { /// protected JoinFragment MergeOuterJoins(IList associations) { - IList sortedAssociations = new List(); - - var indices = GetTopologicalSortOrder(_dependentAliases); - for (int index = indices.Length - 1; index >= 0; index--) - { - sortedAssociations.Add(associations[indices[index]]); - } - JoinFragment outerjoin = Dialect.CreateOuterJoinFragment(); + var sortedAssociations = GetSortedAssociations(associations); OuterJoinableAssociation last = null; foreach (OuterJoinableAssociation oj in sortedAssociations) { @@ -840,6 +855,25 @@ protected JoinFragment MergeOuterJoins(IList associati return outerjoin; } + private static IList GetSortedAssociations(IList associations) + { + if (associations.Count < 2) + return associations; + + var fields = GetDependentAliases(associations); + if (!fields.Exists(a => a.DependsOn?.Count > 0)) + return associations; + + var indexes = GetTopologicalSortOrder(fields); + var sortedAssociations = new List(associations.Count); + for (int index = indexes.Length - 1; index >= 0; index--) + { + sortedAssociations.Add(associations[indexes[index]]); + } + + return sortedAssociations; + } + /// /// Count the number of instances of IJoinable which are actually /// also instances of ILoadable, or are one-to-many associations