private IReadOnlyList <Func <QueryContext, TRelatedEntitiesLoader> > CreateRelatedEntitiesLoaders <TRelatedEntitiesLoader>( IQuerySource querySource, IEnumerable <INavigation> navigationPath) { var relatedEntitiesLoaders = new List <Func <QueryContext, TRelatedEntitiesLoader> >(); var selectExpression = _queryCompilationContext.FindSelectExpression(querySource); var compositePredicateExpressionVisitor = _compositePredicateExpressionVisitorFactory.Create(); var targetTableExpression = selectExpression.GetTableForQuerySource(querySource); var canProduceInnerJoin = true; var navigationCount = 0; foreach (var navigation in navigationPath) { var queryIndex = _queryIndexes[navigationCount]; navigationCount++; var targetEntityType = navigation.GetTargetType(); var targetTableName = _relationalAnnotationProvider.For(targetEntityType).TableName; var targetTableAlias = _queryCompilationContext .CreateUniqueTableAlias(targetTableName[0].ToString().ToLowerInvariant()); if (!navigation.IsCollection()) { var joinedTableExpression = new TableExpression( targetTableName, _relationalAnnotationProvider.For(targetEntityType).Schema, targetTableAlias, querySource); var valueBufferOffset = selectExpression.Projection.Count; canProduceInnerJoin = canProduceInnerJoin && navigation.ForeignKey.IsRequired && navigation.IsDependentToPrincipal(); var joinExpression = canProduceInnerJoin ? selectExpression.AddInnerJoin(joinedTableExpression) : selectExpression.AddLeftOuterJoin(joinedTableExpression); var oldPredicate = selectExpression.Predicate; var materializer = _materializerFactory .CreateMaterializer( targetEntityType, selectExpression, (p, se) => se.AddToProjection( new AliasExpression( new ColumnExpression( _relationalAnnotationProvider.For(p).ColumnName, p, joinedTableExpression))) - valueBufferOffset, querySource: null); if (selectExpression.Predicate != oldPredicate) { selectExpression.Predicate = compositePredicateExpressionVisitor .Visit(selectExpression.Predicate); var newJoinExpression = AdjustJoinExpression(selectExpression, joinExpression); selectExpression.Predicate = oldPredicate; selectExpression.RemoveTable(joinExpression); selectExpression.AddTable(newJoinExpression, createUniqueAlias: false); joinExpression = newJoinExpression; } joinExpression.Predicate = BuildJoinEqualityExpression( navigation, navigation.IsDependentToPrincipal() ? targetTableExpression : joinExpression, navigation.IsDependentToPrincipal() ? joinExpression : targetTableExpression, querySource); targetTableExpression = joinedTableExpression; relatedEntitiesLoaders.Add(qc => (TRelatedEntitiesLoader)_queryCompilationContext.QueryMethodProvider .CreateReferenceRelatedEntitiesLoaderMethod .Invoke( null, new object[] { valueBufferOffset, queryIndex, materializer.Compile() // TODO: Used cached materializer? })); } else { var principalTable = selectExpression.Tables.Count == 1 && selectExpression.Tables .OfType <SelectExpression>() .Any(s => s.Tables.Any(t => t.QuerySource == querySource)) // true when select is wrapped e.g. when RowNumber paging is enabled ? selectExpression.Tables[0] : selectExpression.Tables.Last(t => t.QuerySource == querySource); var canGenerateExists = (selectExpression.Predicate != null || selectExpression.Offset == null) && !IsOrderingOnNonPrincipalKeyProperties( selectExpression.OrderBy, navigation.ForeignKey.PrincipalKey.Properties); foreach (var property in navigation.ForeignKey.PrincipalKey.Properties) { selectExpression .AddToOrderBy( _relationalAnnotationProvider.For(property).ColumnName, property, principalTable, OrderingDirection.Asc); } var targetSelectExpression = _selectExpressionFactory.Create(_queryCompilationContext); targetTableExpression = new TableExpression( targetTableName, _relationalAnnotationProvider.For(targetEntityType).Schema, targetTableAlias, querySource); targetSelectExpression.AddTable(targetTableExpression, createUniqueAlias: false); var materializer = _materializerFactory .CreateMaterializer( targetEntityType, targetSelectExpression, (p, se) => se.AddToProjection( _relationalAnnotationProvider.For(p).ColumnName, p, querySource), querySource: null); if (canGenerateExists) { var subqueryExpression = selectExpression.Clone(); subqueryExpression.ClearProjection(); subqueryExpression.ClearOrderBy(); subqueryExpression.IsProjectStar = false; var subqueryTable = subqueryExpression.Tables.Count == 1 && subqueryExpression.Tables .OfType <SelectExpression>() .Any(s => s.Tables.Any(t => t.QuerySource == querySource)) // true when select is wrapped e.g. when RowNumber paging is enabled ? subqueryExpression.Tables[0] : subqueryExpression.Tables.Last(t => t.QuerySource == querySource); var existsPredicateExpression = new ExistsExpression(subqueryExpression); AddToPredicate(targetSelectExpression, existsPredicateExpression); AddToPredicate(subqueryExpression, BuildJoinEqualityExpression(navigation, targetTableExpression, subqueryTable, querySource)); subqueryExpression.Predicate = compositePredicateExpressionVisitor .Visit(subqueryExpression.Predicate); var pkPropertiesToFkPropertiesMap = navigation.ForeignKey.PrincipalKey.Properties .Zip(navigation.ForeignKey.Properties, (k, v) => new { PkProperty = k, FkProperty = v }) .ToDictionary(x => x.PkProperty, x => x.FkProperty); foreach (var ordering in selectExpression.OrderBy) { // ReSharper disable once PossibleNullReferenceException var principalKeyProperty = ((ordering.Expression as AliasExpression)?.Expression as ColumnExpression).Property; var referencedForeignKeyProperty = pkPropertiesToFkPropertiesMap[principalKeyProperty]; targetSelectExpression .AddToOrderBy( _relationalAnnotationProvider.For(referencedForeignKeyProperty).ColumnName, referencedForeignKeyProperty, targetTableExpression, ordering.OrderingDirection); } } else { var innerJoinSelectExpression = selectExpression.Clone( selectExpression.OrderBy .Select(o => o.Expression) .Last(o => o.IsAliasWithColumnExpression()) .TryGetColumnExpression().TableAlias); innerJoinSelectExpression.ClearProjection(); var innerJoinExpression = targetSelectExpression.AddInnerJoin(innerJoinSelectExpression); LiftOrderBy(innerJoinSelectExpression, targetSelectExpression, innerJoinExpression); innerJoinSelectExpression.IsDistinct = true; innerJoinExpression.Predicate = BuildJoinEqualityExpression( navigation, targetTableExpression, innerJoinExpression, querySource); } targetSelectExpression.Predicate = compositePredicateExpressionVisitor .Visit(targetSelectExpression.Predicate); selectExpression = targetSelectExpression; relatedEntitiesLoaders.Add(qc => (TRelatedEntitiesLoader)_queryCompilationContext.QueryMethodProvider .CreateCollectionRelatedEntitiesLoaderMethod .Invoke( null, new object[] { qc, _shaperCommandContextFactory.Create(() => _querySqlGeneratorFactory.CreateDefault(targetSelectExpression)), queryIndex, materializer.Compile() // TODO: Used cached materializer? })); } } return(relatedEntitiesLoaders); }
private IEnumerable <Expression> CreateIncludeRelatedValuesStrategyFactories( IQuerySource querySource, IEnumerable <INavigation> navigationPath) { var selectExpression = _queryCompilationContext.FindSelectExpression(querySource); var targetTableExpression = selectExpression.FindTableForQuerySource(querySource); var readerIndex = 0; var canProduceInnerJoin = true; foreach (var navigation in navigationPath) { var targetEntityType = navigation.GetTargetType(); var targetTableName = _queryCompilationContext.GetTableName(targetEntityType); var targetTableAlias = targetTableName[0].ToString().ToLower(); if (!navigation.IsCollection()) { var joinedTableExpression = new TableExpression( targetTableName, _queryCompilationContext.GetSchema(targetEntityType), targetTableAlias, querySource); var valueBufferOffset = selectExpression.Projection.Count; canProduceInnerJoin = canProduceInnerJoin && (navigation.ForeignKey.IsRequired && navigation.PointsToPrincipal()); var joinExpression = canProduceInnerJoin ? selectExpression .AddInnerJoin(joinedTableExpression) : selectExpression .AddOuterJoin(joinedTableExpression); var materializer = new MaterializerFactory( _queryCompilationContext.EntityMaterializerSource) .CreateMaterializer( targetEntityType, selectExpression, projectionAdder: (p, se) => se.AddToProjection( new AliasExpression( new ColumnExpression( _queryCompilationContext.GetColumnName(p), p, joinedTableExpression))) - valueBufferOffset, querySource: null); joinExpression.Predicate = BuildJoinEqualityExpression( navigation, (navigation.PointsToPrincipal() ? targetEntityType : navigation.EntityType) .GetPrimaryKey().Properties, navigation.PointsToPrincipal() ? targetTableExpression : joinExpression, navigation.PointsToPrincipal() ? joinExpression : targetTableExpression); targetTableExpression = joinedTableExpression; yield return (Expression.Lambda( Expression.Call( _queryCompilationContext.QueryMethodProvider .CreateReferenceIncludeRelatedValuesStrategyMethod, Expression.Convert( EntityQueryModelVisitor.QueryContextParameter, typeof(RelationalQueryContext)), Expression.Constant(valueBufferOffset), Expression.Constant(readerIndex), materializer))); } else { var principalTable = selectExpression.Tables.Last(t => t.QuerySource == querySource); foreach (var property in navigation.EntityType.GetPrimaryKey().Properties) { selectExpression .AddToOrderBy( _queryCompilationContext.GetColumnName(property), property, principalTable, OrderingDirection.Asc); } var targetSelectExpression = new SelectExpression(); targetTableExpression = new TableExpression( targetTableName, _queryCompilationContext.GetSchema(targetEntityType), targetTableAlias, querySource); targetSelectExpression.AddTable(targetTableExpression); var materializer = new MaterializerFactory( _queryCompilationContext.EntityMaterializerSource) .CreateMaterializer( targetEntityType, targetSelectExpression, (p, se) => se.AddToProjection( _queryCompilationContext.GetColumnName(p), p, querySource), querySource: null); var innerJoinSelectExpression = selectExpression.Clone( selectExpression.OrderBy.Select(o => o.Expression).Last(o => o.IsAliasWithColumnExpression()) .TryGetColumnExpression().TableAlias); innerJoinSelectExpression.IsDistinct = true; innerJoinSelectExpression.ClearProjection(); foreach (var expression in innerJoinSelectExpression.OrderBy .Select(o => o.Expression)) { innerJoinSelectExpression.AddToProjection(expression); } innerJoinSelectExpression.ClearOrderBy(); var primaryKeyProperties = navigation.EntityType.GetPrimaryKey().Properties; var innerJoinExpression = targetSelectExpression.AddInnerJoin(innerJoinSelectExpression); targetSelectExpression.UpdateOrderByColumnBinding(selectExpression.OrderBy, innerJoinExpression); innerJoinExpression.Predicate = BuildJoinEqualityExpression( navigation, primaryKeyProperties, targetTableExpression, innerJoinExpression); selectExpression = targetSelectExpression; readerIndex++; yield return (Expression.Lambda( Expression.Call( _queryCompilationContext.QueryMethodProvider .CreateCollectionIncludeRelatedValuesStrategyMethod, Expression.Call( _queryCompilationContext.QueryMethodProvider.QueryMethod, EntityQueryModelVisitor.QueryContextParameter, Expression.Constant( new CommandBuilder( () => _queryCompilationContext.CreateSqlQueryGenerator(targetSelectExpression), _queryCompilationContext.ValueBufferFactoryFactory))), materializer))); } } }
private IEnumerable <Expression> CreateIncludeRelatedValuesStrategyFactories( IQuerySource querySource, IEnumerable <INavigation> navigationPath) { var selectExpression = _queryCompilationContext.FindSelectExpression(querySource); var targetTableExpression = selectExpression.GetTableForQuerySource(querySource); var canProduceInnerJoin = true; var navigationCount = 0; foreach (var navigation in navigationPath) { var queryIndex = _queryIndexes[navigationCount]; navigationCount++; var targetEntityType = navigation.GetTargetType(); var targetTableName = _relationalAnnotationProvider.For(targetEntityType).TableName; var targetTableAlias = targetTableName[0].ToString().ToLower(); if (!navigation.IsCollection()) { var joinedTableExpression = new TableExpression( targetTableName, _relationalAnnotationProvider.For(targetEntityType).Schema, targetTableAlias, querySource); var valueBufferOffset = selectExpression.Projection.Count; canProduceInnerJoin = canProduceInnerJoin && navigation.ForeignKey.IsRequired && navigation.IsDependentToPrincipal(); var joinExpression = canProduceInnerJoin ? selectExpression .AddInnerJoin(joinedTableExpression) : selectExpression .AddOuterJoin(joinedTableExpression); var oldPredicate = selectExpression.Predicate; var materializer = _materializerFactory .CreateMaterializer( targetEntityType, selectExpression, (p, se) => se.AddToProjection( new AliasExpression( new ColumnExpression( _relationalAnnotationProvider.For(p).ColumnName, p, joinedTableExpression))) - valueBufferOffset, querySource: null); if (selectExpression.Predicate != oldPredicate) { var newJoinExpression = AdjustJoinExpression(selectExpression, joinExpression); selectExpression.Predicate = oldPredicate; selectExpression.RemoveTable(joinExpression); selectExpression.AddTable(newJoinExpression); joinExpression = newJoinExpression; } joinExpression.Predicate = BuildJoinEqualityExpression( navigation, navigation.IsDependentToPrincipal() ? targetTableExpression : joinExpression, navigation.IsDependentToPrincipal() ? joinExpression : targetTableExpression, querySource); targetTableExpression = joinedTableExpression; yield return (Expression.Lambda( Expression.Call( _queryCompilationContext.QueryMethodProvider .CreateReferenceIncludeRelatedValuesStrategyMethod, Expression.Convert( EntityQueryModelVisitor.QueryContextParameter, typeof(RelationalQueryContext)), Expression.Constant(valueBufferOffset), Expression.Constant(queryIndex), materializer))); } else { var principalTable = selectExpression.Tables.Last(t => t.QuerySource == querySource); foreach (var property in navigation.ForeignKey.PrincipalKey.Properties) { selectExpression .AddToOrderBy( _relationalAnnotationProvider.For(property).ColumnName, property, principalTable, OrderingDirection.Asc); } var targetSelectExpression = _selectExpressionFactory.Create(); targetTableExpression = new TableExpression( targetTableName, _relationalAnnotationProvider.For(targetEntityType).Schema, targetTableAlias, querySource); targetSelectExpression.AddTable(targetTableExpression); var materializer = _materializerFactory .CreateMaterializer( targetEntityType, targetSelectExpression, (p, se) => se.AddToProjection( _relationalAnnotationProvider.For(p).ColumnName, p, querySource), querySource: null); var innerJoinSelectExpression = selectExpression.Clone( selectExpression.OrderBy .Select(o => o.Expression) .Last(o => o.IsAliasWithColumnExpression()) .TryGetColumnExpression().TableAlias); innerJoinSelectExpression.IsDistinct = true; innerJoinSelectExpression.ClearProjection(); var innerJoinExpression = targetSelectExpression.AddInnerJoin(innerJoinSelectExpression); LiftOrderBy(innerJoinSelectExpression, targetSelectExpression, innerJoinExpression); innerJoinExpression.Predicate = BuildJoinEqualityExpression( navigation, targetTableExpression, innerJoinExpression, querySource); selectExpression = targetSelectExpression; yield return (Expression.Lambda( Expression.Call( _queryCompilationContext.QueryMethodProvider .CreateCollectionIncludeRelatedValuesStrategyMethod, Expression.Call( _queryCompilationContext.QueryMethodProvider.QueryMethod, EntityQueryModelVisitor.QueryContextParameter, Expression.Constant( _shaperCommandContextFactory.Create( () => _querySqlGeneratorFactory .CreateDefault(targetSelectExpression))), Expression.Constant(queryIndex, typeof(int?))), materializer))); } } }
private IEnumerable <Expression> CreateIncludeRelatedValuesStrategyFactories( IQuerySource querySource, IEnumerable <INavigation> navigationPath) { var selectExpression = _queryCompilationContext.FindSelectExpression(querySource); var targetTableExpression = selectExpression.FindTableForQuerySource(querySource); var readerIndex = 0; foreach (var navigation in navigationPath) { if (!navigation.IsCollection()) { var targetEntityType = navigation.GetTargetType(); var targetTableName = _queryCompilationContext.GetTableName(targetEntityType); var targetTableAlias = targetTableName.First().ToString().ToLower(); var joinedTableExpression = new TableExpression( targetTableName, _queryCompilationContext.GetSchema(targetEntityType), targetTableAlias, querySource); var readerOffset = selectExpression.Projection.Count; var columnExpressions = targetEntityType.Properties .Select(p => new ColumnExpression( _queryCompilationContext.GetColumnName(p), p, joinedTableExpression)); var joinExpression = navigation.ForeignKey.IsRequired && navigation.PointsToPrincipal ? selectExpression .AddInnerJoin(joinedTableExpression, columnExpressions) : selectExpression .AddOuterJoin(joinedTableExpression, columnExpressions); joinExpression.Predicate = BuildJoinEqualityExpression( navigation, (navigation.PointsToPrincipal ? targetEntityType : navigation.EntityType) .GetPrimaryKey().Properties, navigation.PointsToPrincipal ? targetTableExpression : joinExpression, navigation.PointsToPrincipal ? joinExpression : targetTableExpression); targetTableExpression = joinedTableExpression; yield return (Expression.Lambda( Expression.Call( _queryCompilationContext.QueryMethodProvider .CreateReferenceIncludeRelatedValuesStrategyMethod, Expression.Convert(EntityQueryModelVisitor.QueryContextParameter, typeof(RelationalQueryContext)), Expression.Constant(readerIndex), Expression.Constant(readerOffset)))); } else { var principalTable = selectExpression.Tables.Last(t => t.QuerySource == querySource); foreach (var property in navigation.EntityType.GetPrimaryKey().Properties) { selectExpression .AddToOrderBy( _queryCompilationContext.GetColumnName(property), property, principalTable, OrderingDirection.Asc); } var targetEntityType = navigation.GetTargetType(); var targetTableName = _queryCompilationContext.GetTableName(targetEntityType); var targetTableAlias = targetTableName.First().ToString().ToLower(); var targetSelectExpression = new SelectExpression(); targetTableExpression = new TableExpression( targetTableName, _queryCompilationContext.GetSchema(targetEntityType), targetTableAlias, querySource); targetSelectExpression.AddTable(targetTableExpression); foreach (var property in targetEntityType.Properties) { targetSelectExpression .AddToProjection( _queryCompilationContext.GetColumnName(property), property, querySource); } var innerJoinSelectExpression = selectExpression.Clone( ((ColumnExpression)selectExpression.OrderBy.Last().Expression).TableAlias); innerJoinSelectExpression.IsDistinct = true; innerJoinSelectExpression.ClearProjection(); foreach (var columnExpression in innerJoinSelectExpression.OrderBy .Select(o => o.Expression) .Cast <ColumnExpression>()) { innerJoinSelectExpression.AddToProjection(columnExpression); } innerJoinSelectExpression.ClearOrderBy(); var primaryKeyProperties = navigation.EntityType.GetPrimaryKey().Properties; var innerJoinExpression = targetSelectExpression.AddInnerJoin(innerJoinSelectExpression); foreach (var ordering in selectExpression.OrderBy) { var columnExpression = (ColumnExpression)ordering.Expression; targetSelectExpression .AddToOrderBy( columnExpression.Name, columnExpression.Property, innerJoinExpression, ordering.OrderingDirection); } innerJoinExpression.Predicate = BuildJoinEqualityExpression( navigation, primaryKeyProperties, targetTableExpression, innerJoinExpression); var readerParameter = Expression.Parameter(typeof(DbDataReader)); selectExpression = targetSelectExpression; readerIndex++; yield return (Expression.Lambda( Expression.Call( _queryCompilationContext.QueryMethodProvider .CreateCollectionIncludeRelatedValuesStrategyMethod, Expression.Call( _queryCompilationContext.QueryMethodProvider.QueryMethod .MakeGenericMethod(typeof(IValueReader)), EntityQueryModelVisitor.QueryContextParameter, Expression.Constant(new CommandBuilder(targetSelectExpression, _queryCompilationContext)), Expression.Lambda( Expression.Call( _createValueReaderForIncludeMethodInfo, EntityQueryModelVisitor.QueryContextParameter, readerParameter, Expression.Constant(targetEntityType)), readerParameter))))); } } }