diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index a6ac6b2dd14..27f76828361 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -987,9 +987,6 @@ private sealed class SharedTypeEntityExpandingExpressionVisitor : ExpressionVisi private static readonly MethodInfo ObjectEqualsMethodInfo = typeof(object).GetRequiredRuntimeMethod(nameof(object.Equals), typeof(object), typeof(object)); - private static readonly bool _useOldBehavior = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue26592", out var enabled) - && enabled; - private readonly RelationalSqlTranslatingExpressionVisitor _sqlTranslator; private readonly ISqlExpressionFactory _sqlExpressionFactory; @@ -1011,13 +1008,6 @@ public Expression Expand(SelectExpression selectExpression, Expression lambdaBod _selectExpression = selectExpression; _deferredOwnedExpansionRemover = new DeferredOwnedExpansionRemovingVisitor(_selectExpression); - if (_useOldBehavior) - { - return _deferredOwnedExpansionRemover.Visit(Visit(lambdaBody)); - } - - _deferredOwnedExpansionRemover = new DeferredOwnedExpansionRemovingVisitor(_selectExpression); - return _deferredOwnedExpansionRemover.Visit(Visit(lambdaBody)); } @@ -1050,394 +1040,197 @@ protected override Expression VisitExtension(Expression extensionExpression) private Expression? TryExpand(Expression? source, MemberIdentity member) { - if (_useOldBehavior) + source = source.UnwrapTypeConversion(out var convertedType); + var doee = source as DeferredOwnedExpansionExpression; + if (doee is not null) { - source = source.UnwrapTypeConversion(out var convertedType); - var doee = source as DeferredOwnedExpansionExpression; - if (doee is not null) - { - source = _deferredOwnedExpansionRemover.UnwrapDeferredEntityProjectionExpression(doee); - } - - if (source is not EntityShaperExpression entityShaperExpression) - { - return null; - } - - var entityType = entityShaperExpression.EntityType; - if (convertedType != null) - { - entityType = entityType.GetRootType().GetDerivedTypesInclusive() - .FirstOrDefault(et => et.ClrType == convertedType); - - if (entityType == null) - { - return null; - } - } + source = _deferredOwnedExpansionRemover.UnwrapDeferredEntityProjectionExpression(doee); + } - var navigation = member.MemberInfo != null - ? entityType.FindNavigation(member.MemberInfo) - : entityType.FindNavigation(member.Name!); + if (source is not EntityShaperExpression entityShaperExpression) + { + return null; + } - if (navigation == null) - { - return null; - } + var entityType = entityShaperExpression.EntityType; + if (convertedType != null) + { + entityType = entityType.GetRootType().GetDerivedTypesInclusive() + .FirstOrDefault(et => et.ClrType == convertedType); - var targetEntityType = navigation.TargetEntityType; - if (targetEntityType == null - || !targetEntityType.IsOwned()) + if (entityType == null) { return null; } + } - var foreignKey = navigation.ForeignKey; - if (navigation.IsCollection) - { - var innerShapedQuery = CreateShapedQueryExpression( - targetEntityType, _sqlExpressionFactory.Select(targetEntityType)); - - var makeNullable = foreignKey.PrincipalKey.Properties - .Concat(foreignKey.Properties) - .Select(p => p.ClrType) - .Any(t => t.IsNullableType()); - - var innerSequenceType = innerShapedQuery.Type.GetSequenceType(); - var correlationPredicateParameter = Expression.Parameter(innerSequenceType); - - var outerKey = entityShaperExpression.CreateKeyValuesExpression( - navigation.IsOnDependent - ? foreignKey.Properties - : foreignKey.PrincipalKey.Properties, - makeNullable); - var innerKey = correlationPredicateParameter.CreateKeyValuesExpression( - navigation.IsOnDependent - ? foreignKey.PrincipalKey.Properties - : foreignKey.Properties, - makeNullable); - - var keyComparison = Expression.Call( - ObjectEqualsMethodInfo, AddConvertToObject(outerKey), AddConvertToObject(innerKey)); - - var predicate = makeNullable - ? Expression.AndAlso( - outerKey is NewArrayExpression newArrayExpression - ? newArrayExpression.Expressions - .Select( - e => - { - var left = (e as UnaryExpression)?.Operand ?? e; - - return Expression.NotEqual(left, Expression.Constant(null, left.Type)); - }) - .Aggregate((l, r) => Expression.AndAlso(l, r)) - : Expression.NotEqual(outerKey, Expression.Constant(null, outerKey.Type)), - keyComparison) - : (Expression)keyComparison; - - var correlationPredicate = Expression.Lambda(predicate, correlationPredicateParameter); - - return Expression.Call( - QueryableMethods.Where.MakeGenericMethod(innerSequenceType), - innerShapedQuery, - Expression.Quote(correlationPredicate)); - } - - var entityProjectionExpression = GetEntityProjectionExpression(entityShaperExpression); - var innerShaper = entityProjectionExpression.BindNavigation(navigation); - if (innerShaper == null) - { - // Owned types don't support inheritance See https://github.com/dotnet/efcore/issues/9630 - // So there is no handling for dependent having TPT - // If navigation is defined on derived type and entity type is part of TPT then we need to get ITableBase for derived type. - // TODO: The following code should also handle Function and SqlQuery mappings - var table = navigation.DeclaringEntityType.BaseType == null - || entityType.FindDiscriminatorProperty() != null - ? navigation.DeclaringEntityType.GetViewOrTableMappings().Single().Table - : navigation.DeclaringEntityType.GetViewOrTableMappings().Select(tm => tm.Table) - .Except(navigation.DeclaringEntityType.BaseType.GetViewOrTableMappings().Select(tm => tm.Table)) - .Single(); - if (table.GetReferencingRowInternalForeignKeys(foreignKey.PrincipalEntityType)?.Contains(foreignKey) == true) - { - // Mapped to same table - // We get identifying column to figure out tableExpression to pull columns from and nullability of most principal side - var identifyingColumn = entityProjectionExpression.BindProperty(entityType.FindPrimaryKey()!.Properties.First()); - var principalNullable = identifyingColumn.IsNullable - // Also make nullable if navigation is on derived type and and principal is TPT - // Since identifying PK would be non-nullable but principal can still be null - // Derived owned navigation does not de-dupe the PK column which for principal is from base table - // and for dependent on derived table - || (entityType.FindDiscriminatorProperty() == null - && navigation.DeclaringEntityType.IsStrictlyDerivedFrom(entityShaperExpression.EntityType)); - - var entityProjection = _selectExpression.GenerateWeakEntityProjectionExpression( - targetEntityType, table, identifyingColumn.Name, identifyingColumn.Table, principalNullable); - - if (entityProjection != null) - { - innerShaper = new RelationalEntityShaperExpression(targetEntityType, entityProjection, principalNullable); - } - } - - if (innerShaper == null) - { - // InnerShaper is still null if either it is not table sharing or we failed to find table to pick data from - // So we find the table it is mapped to and generate join with it. - // Owned types don't support inheritance See https://github.com/dotnet/efcore/issues/9630 - // So there is no handling for dependent having TPT - table = targetEntityType.GetViewOrTableMappings().Single().Table; - var innerSelectExpression = _sqlExpressionFactory.Select(targetEntityType); - var innerShapedQuery = CreateShapedQueryExpression(targetEntityType, innerSelectExpression); - - var makeNullable = foreignKey.PrincipalKey.Properties - .Concat(foreignKey.Properties) - .Select(p => p.ClrType) - .Any(t => t.IsNullableType()); - - var outerKey = entityShaperExpression.CreateKeyValuesExpression( - navigation.IsOnDependent - ? foreignKey.Properties - : foreignKey.PrincipalKey.Properties, - makeNullable); - var innerKey = innerShapedQuery.ShaperExpression.CreateKeyValuesExpression( - navigation.IsOnDependent - ? foreignKey.PrincipalKey.Properties - : foreignKey.Properties, - makeNullable); - - var joinPredicate = _sqlTranslator.Translate(Expression.Equal(outerKey, innerKey))!; - // Following conditions should match conditions for pushdown on outer during SelectExpression.AddJoin method - var pushdownRequired = _selectExpression.Limit != null - || _selectExpression.Offset != null - || _selectExpression.IsDistinct - || _selectExpression.GroupBy.Count > 0; - _selectExpression.AddLeftJoin(innerSelectExpression, joinPredicate); - - // If pushdown was required on SelectExpression then we need to fetch the updated entity projection - if (pushdownRequired) - { - if (doee is not null) - { - entityShaperExpression = _deferredOwnedExpansionRemover.UnwrapDeferredEntityProjectionExpression(doee); - } - - entityProjectionExpression = GetEntityProjectionExpression(entityShaperExpression); - } - - var leftJoinTable = _selectExpression.Tables.Last(); - - innerShaper = new RelationalEntityShaperExpression( - targetEntityType, - _selectExpression.GenerateWeakEntityProjectionExpression( - targetEntityType, table, null, leftJoinTable, nullable: true)!, - nullable: true); - } - - entityProjectionExpression.AddNavigationBinding(navigation, innerShaper); - } + var navigation = member.MemberInfo != null + ? entityType.FindNavigation(member.MemberInfo) + : entityType.FindNavigation(member.Name!); - return doee is not null - ? doee.AddNavigation(targetEntityType, navigation) - : new DeferredOwnedExpansionExpression( - targetEntityType, - (ProjectionBindingExpression)entityShaperExpression.ValueBufferExpression, - navigation); + if (navigation == null) + { + return null; } - else + + var targetEntityType = navigation.TargetEntityType; + if (targetEntityType == null + || !targetEntityType.IsOwned()) { - source = source.UnwrapTypeConversion(out var convertedType); - var doee = source as DeferredOwnedExpansionExpression; - if (doee is not null) - { - source = _deferredOwnedExpansionRemover.UnwrapDeferredEntityProjectionExpression(doee); - } + return null; + } - if (source is not EntityShaperExpression entityShaperExpression) - { - return null; - } + var foreignKey = navigation.ForeignKey; + if (navigation.IsCollection) + { + var innerShapedQuery = CreateShapedQueryExpression( + targetEntityType, _sqlExpressionFactory.Select(targetEntityType)); + + var makeNullable = foreignKey.PrincipalKey.Properties + .Concat(foreignKey.Properties) + .Select(p => p.ClrType) + .Any(t => t.IsNullableType()); + + var innerSequenceType = innerShapedQuery.Type.GetSequenceType(); + var correlationPredicateParameter = Expression.Parameter(innerSequenceType); + + var outerKey = entityShaperExpression.CreateKeyValuesExpression( + navigation.IsOnDependent + ? foreignKey.Properties + : foreignKey.PrincipalKey.Properties, + makeNullable); + var innerKey = correlationPredicateParameter.CreateKeyValuesExpression( + navigation.IsOnDependent + ? foreignKey.PrincipalKey.Properties + : foreignKey.Properties, + makeNullable); + + var keyComparison = Expression.Call( + ObjectEqualsMethodInfo, AddConvertToObject(outerKey), AddConvertToObject(innerKey)); + + var predicate = makeNullable + ? Expression.AndAlso( + outerKey is NewArrayExpression newArrayExpression + ? newArrayExpression.Expressions + .Select( + e => + { + var left = (e as UnaryExpression)?.Operand ?? e; + + return Expression.NotEqual(left, Expression.Constant(null, left.Type)); + }) + .Aggregate((l, r) => Expression.AndAlso(l, r)) + : Expression.NotEqual(outerKey, Expression.Constant(null, outerKey.Type)), + keyComparison) + : (Expression)keyComparison; + + var correlationPredicate = Expression.Lambda(predicate, correlationPredicateParameter); + + return Expression.Call( + QueryableMethods.Where.MakeGenericMethod(innerSequenceType), + innerShapedQuery, + Expression.Quote(correlationPredicate)); + } - var entityType = entityShaperExpression.EntityType; - if (convertedType != null) + var entityProjectionExpression = GetEntityProjectionExpression(entityShaperExpression); + var innerShaper = entityProjectionExpression.BindNavigation(navigation); + if (innerShaper == null) + { + // Owned types don't support inheritance See https://github.com/dotnet/efcore/issues/9630 + // So there is no handling for dependent having TPT + // If navigation is defined on derived type and entity type is part of TPT then we need to get ITableBase for derived type. + // TODO: The following code should also handle Function and SqlQuery mappings + var table = navigation.DeclaringEntityType.BaseType == null + || entityType.FindDiscriminatorProperty() != null + ? navigation.DeclaringEntityType.GetViewOrTableMappings().Single().Table + : navigation.DeclaringEntityType.GetViewOrTableMappings().Select(tm => tm.Table) + .Except(navigation.DeclaringEntityType.BaseType.GetViewOrTableMappings().Select(tm => tm.Table)) + .Single(); + if (table.GetReferencingRowInternalForeignKeys(foreignKey.PrincipalEntityType)?.Contains(foreignKey) == true) { - entityType = entityType.GetRootType().GetDerivedTypesInclusive() - .FirstOrDefault(et => et.ClrType == convertedType); - - if (entityType == null) + // Mapped to same table + // We get identifying column to figure out tableExpression to pull columns from and nullability of most principal side + var identifyingColumn = entityProjectionExpression.BindProperty(entityType.FindPrimaryKey()!.Properties.First()); + var principalNullable = identifyingColumn.IsNullable + // Also make nullable if navigation is on derived type and and principal is TPT + // Since identifying PK would be non-nullable but principal can still be null + // Derived owned navigation does not de-dupe the PK column which for principal is from base table + // and for dependent on derived table + || (entityType.FindDiscriminatorProperty() == null + && navigation.DeclaringEntityType.IsStrictlyDerivedFrom(entityShaperExpression.EntityType)); + + var entityProjection = _selectExpression.GenerateWeakEntityProjectionExpression( + targetEntityType, table, identifyingColumn.Name, identifyingColumn.Table, principalNullable); + + if (entityProjection != null) { - return null; + innerShaper = new RelationalEntityShaperExpression(targetEntityType, entityProjection, principalNullable); } } - var navigation = member.MemberInfo != null - ? entityType.FindNavigation(member.MemberInfo) - : entityType.FindNavigation(member.Name!); - - if (navigation == null) - { - return null; - } - - var targetEntityType = navigation.TargetEntityType; - if (targetEntityType == null - || !targetEntityType.IsOwned()) - { - return null; - } - - var foreignKey = navigation.ForeignKey; - if (navigation.IsCollection) + if (innerShaper == null) { - var innerShapedQuery = CreateShapedQueryExpression( - targetEntityType, _sqlExpressionFactory.Select(targetEntityType)); + // InnerShaper is still null if either it is not table sharing or we failed to find table to pick data from + // So we find the table it is mapped to and generate join with it. + // Owned types don't support inheritance See https://github.com/dotnet/efcore/issues/9630 + // So there is no handling for dependent having TPT + table = targetEntityType.GetViewOrTableMappings().Single().Table; + var innerSelectExpression = _sqlExpressionFactory.Select(targetEntityType); + var innerShapedQuery = CreateShapedQueryExpression(targetEntityType, innerSelectExpression); var makeNullable = foreignKey.PrincipalKey.Properties .Concat(foreignKey.Properties) .Select(p => p.ClrType) .Any(t => t.IsNullableType()); - var innerSequenceType = innerShapedQuery.Type.GetSequenceType(); - var correlationPredicateParameter = Expression.Parameter(innerSequenceType); - var outerKey = entityShaperExpression.CreateKeyValuesExpression( navigation.IsOnDependent ? foreignKey.Properties : foreignKey.PrincipalKey.Properties, makeNullable); - var innerKey = correlationPredicateParameter.CreateKeyValuesExpression( + var innerKey = innerShapedQuery.ShaperExpression.CreateKeyValuesExpression( navigation.IsOnDependent ? foreignKey.PrincipalKey.Properties : foreignKey.Properties, makeNullable); - var keyComparison = Expression.Call( - ObjectEqualsMethodInfo, AddConvertToObject(outerKey), AddConvertToObject(innerKey)); - - var predicate = makeNullable - ? Expression.AndAlso( - outerKey is NewArrayExpression newArrayExpression - ? newArrayExpression.Expressions - .Select( - e => - { - var left = (e as UnaryExpression)?.Operand ?? e; - - return Expression.NotEqual(left, Expression.Constant(null, left.Type)); - }) - .Aggregate((l, r) => Expression.AndAlso(l, r)) - : Expression.NotEqual(outerKey, Expression.Constant(null, outerKey.Type)), - keyComparison) - : (Expression)keyComparison; - - var correlationPredicate = Expression.Lambda(predicate, correlationPredicateParameter); - - return Expression.Call( - QueryableMethods.Where.MakeGenericMethod(innerSequenceType), - innerShapedQuery, - Expression.Quote(correlationPredicate)); - } - - var entityProjectionExpression = GetEntityProjectionExpression(entityShaperExpression); - var innerShaper = entityProjectionExpression.BindNavigation(navigation); - if (innerShaper == null) - { - // Owned types don't support inheritance See https://github.com/dotnet/efcore/issues/9630 - // So there is no handling for dependent having TPT - // If navigation is defined on derived type and entity type is part of TPT then we need to get ITableBase for derived type. - // TODO: The following code should also handle Function and SqlQuery mappings - var table = navigation.DeclaringEntityType.BaseType == null - || entityType.FindDiscriminatorProperty() != null - ? navigation.DeclaringEntityType.GetViewOrTableMappings().Single().Table - : navigation.DeclaringEntityType.GetViewOrTableMappings().Select(tm => tm.Table) - .Except(navigation.DeclaringEntityType.BaseType.GetViewOrTableMappings().Select(tm => tm.Table)) - .Single(); - if (table.GetReferencingRowInternalForeignKeys(foreignKey.PrincipalEntityType)?.Contains(foreignKey) == true) - { - // Mapped to same table - // We get identifying column to figure out tableExpression to pull columns from and nullability of most principal side - var identifyingColumn = entityProjectionExpression.BindProperty(entityType.FindPrimaryKey()!.Properties.First()); - var principalNullable = identifyingColumn.IsNullable - // Also make nullable if navigation is on derived type and and principal is TPT - // Since identifying PK would be non-nullable but principal can still be null - // Derived owned navigation does not de-dupe the PK column which for principal is from base table - // and for dependent on derived table - || (entityType.FindDiscriminatorProperty() == null - && navigation.DeclaringEntityType.IsStrictlyDerivedFrom(entityShaperExpression.EntityType)); - - var entityProjection = _selectExpression.GenerateWeakEntityProjectionExpression( - targetEntityType, table, identifyingColumn.Name, identifyingColumn.Table, principalNullable); - - if (entityProjection != null) - { - innerShaper = new RelationalEntityShaperExpression(targetEntityType, entityProjection, principalNullable); - } - } + var joinPredicate = _sqlTranslator.Translate(Expression.Equal(outerKey, innerKey))!; + // Following conditions should match conditions for pushdown on outer during SelectExpression.AddJoin method + var pushdownRequired = _selectExpression.Limit != null + || _selectExpression.Offset != null + || _selectExpression.IsDistinct + || _selectExpression.GroupBy.Count > 0; + _selectExpression.AddLeftJoin(innerSelectExpression, joinPredicate); - if (innerShaper == null) + // If pushdown was required on SelectExpression then we need to fetch the updated entity projection + if (pushdownRequired) { - // InnerShaper is still null if either it is not table sharing or we failed to find table to pick data from - // So we find the table it is mapped to and generate join with it. - // Owned types don't support inheritance See https://github.com/dotnet/efcore/issues/9630 - // So there is no handling for dependent having TPT - table = targetEntityType.GetViewOrTableMappings().Single().Table; - var innerSelectExpression = _sqlExpressionFactory.Select(targetEntityType); - var innerShapedQuery = CreateShapedQueryExpression(targetEntityType, innerSelectExpression); - - var makeNullable = foreignKey.PrincipalKey.Properties - .Concat(foreignKey.Properties) - .Select(p => p.ClrType) - .Any(t => t.IsNullableType()); - - var outerKey = entityShaperExpression.CreateKeyValuesExpression( - navigation.IsOnDependent - ? foreignKey.Properties - : foreignKey.PrincipalKey.Properties, - makeNullable); - var innerKey = innerShapedQuery.ShaperExpression.CreateKeyValuesExpression( - navigation.IsOnDependent - ? foreignKey.PrincipalKey.Properties - : foreignKey.Properties, - makeNullable); - - var joinPredicate = _sqlTranslator.Translate(Expression.Equal(outerKey, innerKey))!; - // Following conditions should match conditions for pushdown on outer during SelectExpression.AddJoin method - var pushdownRequired = _selectExpression.Limit != null - || _selectExpression.Offset != null - || _selectExpression.IsDistinct - || _selectExpression.GroupBy.Count > 0; - _selectExpression.AddLeftJoin(innerSelectExpression, joinPredicate); - - // If pushdown was required on SelectExpression then we need to fetch the updated entity projection - if (pushdownRequired) + if (doee is not null) { - if (doee is not null) - { - entityShaperExpression = _deferredOwnedExpansionRemover.UnwrapDeferredEntityProjectionExpression(doee); - } - - entityProjectionExpression = GetEntityProjectionExpression(entityShaperExpression); + entityShaperExpression = _deferredOwnedExpansionRemover.UnwrapDeferredEntityProjectionExpression(doee); } - var leftJoinTable = _selectExpression.Tables.Last(); - - innerShaper = new RelationalEntityShaperExpression( - targetEntityType, - _selectExpression.GenerateWeakEntityProjectionExpression( - targetEntityType, table, null, leftJoinTable, nullable: true)!, - nullable: true); + entityProjectionExpression = GetEntityProjectionExpression(entityShaperExpression); } - entityProjectionExpression.AddNavigationBinding(navigation, innerShaper); - } + var leftJoinTable = _selectExpression.Tables.Last(); - return doee is not null - ? doee.AddNavigation(targetEntityType, navigation) - : new DeferredOwnedExpansionExpression( + innerShaper = new RelationalEntityShaperExpression( targetEntityType, - (ProjectionBindingExpression)entityShaperExpression.ValueBufferExpression, - navigation); + _selectExpression.GenerateWeakEntityProjectionExpression( + targetEntityType, table, null, leftJoinTable, nullable: true)!, + nullable: true); + } + + entityProjectionExpression.AddNavigationBinding(navigation, innerShaper); } + + return doee is not null + ? doee.AddNavigation(targetEntityType, navigation) + : new DeferredOwnedExpansionExpression( + targetEntityType, + (ProjectionBindingExpression)entityShaperExpression.ValueBufferExpression, + navigation); } private static Expression AddConvertToObject(Expression expression) diff --git a/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs b/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs index 510b6a0d771..56e9626a0e2 100644 --- a/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs +++ b/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs @@ -25,9 +25,6 @@ public class NavigationFixer : INavigationFixer private bool _inFixup; private bool _inAttachGraph; - private readonly bool _useOldBehavior26779 - = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue26779", out var enabled) && enabled; - /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in diff --git a/src/Shared/Multigraph.cs b/src/Shared/Multigraph.cs index aa994729069..f86a8317e79 100644 --- a/src/Shared/Multigraph.cs +++ b/src/Shared/Multigraph.cs @@ -12,9 +12,6 @@ internal class Multigraph : Graph private readonly Dictionary> _successorMap = new(); private readonly Dictionary> _predecessorMap = new(); - private readonly bool _useOldBehavior - = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue26750", out var enabled) && enabled; - public IEnumerable GetEdges(TVertex from, TVertex to) { if (_successorMap.TryGetValue(from, out var successorSet)) @@ -326,9 +323,7 @@ public IReadOnlyList> BatchingTopologicalSort( && tryBreakEdge != null) { var candidateVertex = candidateVertices[candidateIndex]; - if (_useOldBehavior - ? predecessorCounts[candidateVertex] != 1 - : predecessorCounts[candidateVertex] == 0) + if (predecessorCounts[candidateVertex] == 0) { candidateIndex++; continue; @@ -345,8 +340,7 @@ public IReadOnlyList> BatchingTopologicalSort( _successorMap[incomingNeighbor].Remove(candidateVertex); _predecessorMap[candidateVertex].Remove(incomingNeighbor); predecessorCounts[candidateVertex]--; - if (_useOldBehavior - || predecessorCounts[candidateVertex] == 0) + if (predecessorCounts[candidateVertex] == 0) { currentRootsQueue.Add(candidateVertex); nextRootsQueue = new List();