private IEnumerable<Expression> CreateIncludeRelatedValuesStrategyFactories( IQuerySource querySource, IEnumerable<INavigation> navigationPath) { var selectExpression = _queryCompilationContext.FindSelectExpression(querySource); var targetTableExpression = selectExpression.GetTableForQuerySource(querySource); var canProduceInnerJoin = true; var includeReferenceCount = 0; 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() ? targetTableExpression : joinExpression, navigation.PointsToPrincipal() ? joinExpression : targetTableExpression, querySource); targetTableExpression = joinedTableExpression; var readerIndex = _readerIndexes[includeReferenceCount]; includeReferenceCount++; 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.ForeignKey.PrincipalKey.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(); 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( new CommandBuilder( () => _queryCompilationContext.CreateSqlQueryGenerator(targetSelectExpression), _queryCompilationContext.ValueBufferFactoryFactory))), materializer)); } } }
protected override Expression VisitEntityQueryable(Type elementType) { Check.NotNull(elementType, nameof(elementType)); var queryMethodInfo = CreateValueBufferMethodInfo; var relationalQueryCompilationContext = QueryModelVisitor.QueryCompilationContext; var entityType = relationalQueryCompilationContext.Model.GetEntityType(elementType); var selectExpression = new SelectExpression(); var name = relationalQueryCompilationContext.RelationalExtensions.For(entityType).TableName; var tableAlias = _querySource.HasGeneratedItemName() ? name[0].ToString().ToLower() : _querySource.ItemName; var fromSqlAnnotation = relationalQueryCompilationContext .GetCustomQueryAnnotations(RelationalQueryableExtensions.FromSqlMethodInfo) .LastOrDefault(a => a.QuerySource == _querySource); var composable = true; var sqlString = ""; object[] sqlParameters = null; if (fromSqlAnnotation == null) { selectExpression.AddTable( new TableExpression( name, relationalQueryCompilationContext.RelationalExtensions.For(entityType).Schema, tableAlias, _querySource)); } else { sqlString = (string)fromSqlAnnotation.Arguments[1]; sqlParameters = (object[])fromSqlAnnotation.Arguments[2]; selectExpression.AddTable( new RawSqlDerivedTableExpression( sqlString, sqlParameters, tableAlias, _querySource)); var sqlStart = sqlString.SkipWhile(char.IsWhiteSpace).Take(7).ToArray(); if (sqlStart.Length != 7 || !char.IsWhiteSpace(sqlStart.Last()) || !new string(sqlStart).StartsWith("SELECT", StringComparison.OrdinalIgnoreCase)) { if (relationalQueryCompilationContext.QueryAnnotations .OfType <IncludeQueryAnnotation>().Any()) { throw new InvalidOperationException(Strings.StoredProcedureIncludeNotSupported); } QueryModelVisitor.RequiresClientEval = true; composable = false; } if (!fromSqlAnnotation.QueryModel.BodyClauses.Any() && !fromSqlAnnotation.QueryModel.ResultOperators.Any()) { composable = false; } } QueryModelVisitor.AddQuery(_querySource, selectExpression); var queryMethodArguments = new List <Expression> { Expression.Constant(_querySource), EntityQueryModelVisitor.QueryContextParameter, EntityQueryModelVisitor.QueryResultScopeParameter, _valueBufferParameter, Expression.Constant(0) }; if (QueryModelVisitor.QueryCompilationContext .QuerySourceRequiresMaterialization(_querySource) || QueryModelVisitor.RequiresClientEval) { var materializer = new MaterializerFactory( relationalQueryCompilationContext .EntityMaterializerSource) .CreateMaterializer( entityType, selectExpression, (p, se) => se.AddToProjection( relationalQueryCompilationContext.RelationalExtensions.For(p).ColumnName, p, _querySource), QueryModelVisitor.QueryCompilationContext.RelationalExtensions, _querySource); queryMethodInfo = CreateEntityMethodInfo.MakeGenericMethod(elementType); var keyFactory = relationalQueryCompilationContext.EntityKeyFactorySource .GetKeyFactory(entityType.GetPrimaryKey()); queryMethodArguments.AddRange( new[] { Expression.Constant(entityType), Expression.Constant(QueryModelVisitor.QuerySourceRequiresTracking(_querySource)), Expression.Constant(keyFactory), Expression.Constant(entityType.GetPrimaryKey().Properties), materializer }); } Func <ISqlQueryGenerator> sqlQueryGeneratorFactory; if (composable) { sqlQueryGeneratorFactory = () => relationalQueryCompilationContext.CreateSqlQueryGenerator(selectExpression); } else { sqlQueryGeneratorFactory = () => new RawSqlQueryGenerator(selectExpression, sqlString, sqlParameters, relationalQueryCompilationContext.TypeMapper); } return(Expression.Call( relationalQueryCompilationContext.QueryMethodProvider.ShapedQueryMethod .MakeGenericMethod(queryMethodInfo.ReturnType), EntityQueryModelVisitor.QueryContextParameter, Expression.Constant( new CommandBuilder( sqlQueryGeneratorFactory, relationalQueryCompilationContext.ValueBufferFactoryFactory)), Expression.Lambda( Expression.Call(queryMethodInfo, queryMethodArguments), _valueBufferParameter))); }
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 = _queryCompilationContext.RelationalExtensions.For(targetEntityType).TableName; var targetTableAlias = targetTableName[0].ToString().ToLower(); if (!navigation.IsCollection()) { var joinedTableExpression = new TableExpression( targetTableName, _queryCompilationContext.RelationalExtensions.For(targetEntityType).Schema, 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, (p, se) => se.AddToProjection( new AliasExpression( new ColumnExpression( _queryCompilationContext.RelationalExtensions.For(p).ColumnName, p, joinedTableExpression))) - valueBufferOffset, _queryCompilationContext.RelationalExtensions, querySource: null); joinExpression.Predicate = BuildJoinEqualityExpression( navigation, navigation.PointsToPrincipal() ? targetTableExpression : joinExpression, navigation.PointsToPrincipal() ? 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( _queryCompilationContext.RelationalExtensions.For(property).ColumnName, property, principalTable, OrderingDirection.Asc); } var targetSelectExpression = new SelectExpression(); targetTableExpression = new TableExpression( targetTableName, _queryCompilationContext.RelationalExtensions.For(targetEntityType).Schema, targetTableAlias, querySource); targetSelectExpression.AddTable(targetTableExpression); var materializer = new MaterializerFactory( _queryCompilationContext.EntityMaterializerSource) .CreateMaterializer( targetEntityType, targetSelectExpression, (p, se) => se.AddToProjection( _queryCompilationContext.RelationalExtensions.For(p).ColumnName, p, querySource), _queryCompilationContext.RelationalExtensions, 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( new CommandBuilder( () => _queryCompilationContext.CreateSqlQueryGenerator(targetSelectExpression), _queryCompilationContext.ValueBufferFactoryFactory)), Expression.Constant(queryIndex, typeof(int?))), materializer))); } } }