protected override Expression VisitEntityQueryable([NotNull] Type elementType) { Check.NotNull(elementType, nameof(elementType)); var entityType = _model.FindEntityType(elementType); var collectionName = _annotationsProvider.For(entityType).CollectionName; var findExpression = _findExpressionFactory.Create(); findExpression.SetCollectionExpression(new CollectionExpression(collectionName, elementType)); var shaper = CreateShaper(elementType, entityType, findExpression); Func <IBsonQueryGenerator> createQueryGenerator = findExpression.CreateBsonQueryGenerator; var valueBufferShaper = _valueBufferShaperFactory.Create(findExpression); return(Expression.Call( QueryModelVisitor.QueryCompilationContext.QueryMethodProvider .ShapedQueryMethod .MakeGenericMethod(shaper.Type), EntityQueryModelVisitor.QueryContextParameter, Expression.Constant(_shaperCommandContextFactory.Create(createQueryGenerator)), Expression.Constant(shaper), Expression.Constant(valueBufferShaper))); }
/// <summary> /// Resolve read only expression /// </summary> /// <param name="elementType"></param> /// <returns></returns> protected override Expression VisitEntityQueryable([NotNull] Type elementType) { Check.NotNull(elementType, nameof(elementType)); // create then add read only expression var cypherQueryCompilationContext = QueryModelVisitor .QueryCompilationContext; var entityType = cypherQueryCompilationContext.FindEntityType(_querySource) ?? _model.FindEntityType(elementType); var readOnlyExpression = _readOnlyExpressionFactory .Create(cypherQueryCompilationContext); QueryModelVisitor.AddQuery( _querySource, readOnlyExpression ); // unique node alias either from the Relinq query source if not generated or the first label string[] labels = entityType.Cypher().Labels; string alias = cypherQueryCompilationContext .CreateUniqueNodeAlias( _querySource.HasGeneratedItemName() ? labels[0][0].ToString().ToLowerInvariant() : _querySource.ItemName ); // TODO: Use from SQL annotation? // default match readOnlyExpression.AddReadingClause( new MatchExpression( new PatternExpression( new NodePatternExpression( labels, _querySource, alias ) ) ) ); // bundle the sql (cypher) generator inside the shaper command factory Func <IQuerySqlGenerator> querySqlGeneratorFunc = readOnlyExpression .CreateDefaultQueryCypherGenerator; var shaper = CreateShaper(elementType, entityType, readOnlyExpression); return(Expression.Call( QueryModelVisitor.QueryCompilationContext.QueryMethodProvider .ShapedQueryMethod .MakeGenericMethod(shaper.Type), EntityQueryModelVisitor.QueryContextParameter, Expression.Constant(_shaperCommandContextFactory.Create(querySqlGeneratorFunc)), Expression.Constant(shaper))); }
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); }
protected override Expression VisitEntityQueryable(Type elementType) { var relationalQueryCompilationContext = QueryModelVisitor.QueryCompilationContext; var entityType = relationalQueryCompilationContext.FindEntityType(_querySource) ?? _model.FindEntityType(elementType); var selectExpression = _selectExpressionFactory.Create(relationalQueryCompilationContext); QueryModelVisitor.AddQuery(_querySource, selectExpression); var tableName = entityType.Relational().TableName; var databaseName = elementType.CustomAttributes .FirstOrDefault(c => c.AttributeType == typeof(DatabaseNameAttribute)) ?.ConstructorArguments[0] .Value .ToString(); var tableAlias = relationalQueryCompilationContext.CreateUniqueTableAlias( _querySource.HasGeneratedItemName() ? tableName[0].ToString(CultureInfo.InvariantCulture).ToUpperInvariant() : (_querySource as GroupJoinClause)?.JoinClause.ItemName ?? _querySource.ItemName); var fromSqlAnnotation = relationalQueryCompilationContext .QueryAnnotations .OfType <FromSqlResultOperator>() .LastOrDefault(a => a.QuerySource == _querySource); Func <IQuerySqlGenerator> querySqlGeneratorFunc = selectExpression.CreateDefaultQuerySqlGenerator; if (fromSqlAnnotation == null) { selectExpression.AddTable( new TableExpression( databaseName, tableName, entityType.Relational().Schema, tableAlias, _querySource)); } else { selectExpression.AddTable( new FromSqlExpression( fromSqlAnnotation.Sql, fromSqlAnnotation.Arguments, tableAlias, _querySource)); var trimmedSql = fromSqlAnnotation.Sql.TrimStart('\r', '\n', '\t', ' '); var useQueryComposition = trimmedSql.StartsWith("SELECT ", StringComparison.OrdinalIgnoreCase) || trimmedSql.StartsWith("SELECT" + Environment.NewLine, StringComparison.OrdinalIgnoreCase) || trimmedSql.StartsWith("SELECT\t", StringComparison.OrdinalIgnoreCase); var requiresClientEval = !useQueryComposition; if (!useQueryComposition && relationalQueryCompilationContext.IsIncludeQuery) { throw new InvalidOperationException( RelationalStrings.StoredProcedureIncludeNotSupported); } if (useQueryComposition && fromSqlAnnotation.QueryModel.IsIdentityQuery() && !fromSqlAnnotation.QueryModel.ResultOperators.Any() && !relationalQueryCompilationContext.IsIncludeQuery && entityType.BaseType == null && !entityType.GetDerivedTypes().Any()) { useQueryComposition = false; } if (!useQueryComposition) { QueryModelVisitor.RequiresClientEval = requiresClientEval; querySqlGeneratorFunc = () => selectExpression.CreateFromSqlQuerySqlGenerator( fromSqlAnnotation.Sql, fromSqlAnnotation.Arguments); } } var shaper = CreateShaper(elementType, entityType, selectExpression); DiscriminateProjectionQuery(entityType, selectExpression, _querySource); return(Expression.Call( QueryModelVisitor.QueryCompilationContext.QueryMethodProvider .ShapedQueryMethod .MakeGenericMethod(shaper.Type), EntityQueryModelVisitor.QueryContextParameter, Expression.Constant(_shaperCommandContextFactory.Create(querySqlGeneratorFunc)), Expression.Constant(shaper))); }
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))); } } }
protected override Expression VisitEntityQueryable(Type elementType) { Check.NotNull(elementType, nameof(elementType)); var relationalQueryCompilationContext = QueryModelVisitor.QueryCompilationContext; var entityType = _model.FindEntityType(elementType); var selectExpression = _selectExpressionFactory.Create(relationalQueryCompilationContext); QueryModelVisitor.AddQuery(_querySource, selectExpression); var name = _relationalAnnotationProvider.For(entityType).TableName; var tableAlias = _querySource.HasGeneratedItemName() ? name[0].ToString().ToLowerInvariant() : _querySource.ItemName; var fromSqlAnnotation = relationalQueryCompilationContext .QueryAnnotations .OfType <FromSqlResultOperator>() .LastOrDefault(a => a.QuerySource == _querySource); Func <IQuerySqlGenerator> querySqlGeneratorFunc = selectExpression.CreateDefaultQuerySqlGenerator; if (fromSqlAnnotation == null) { selectExpression.AddTable( new TableExpression( name, _relationalAnnotationProvider.For(entityType).Schema, tableAlias, _querySource)); } else { selectExpression.AddTable( new FromSqlExpression( fromSqlAnnotation.Sql, fromSqlAnnotation.Arguments, tableAlias, _querySource)); var useQueryComposition = fromSqlAnnotation.Sql .TrimStart() .StartsWith("SELECT ", StringComparison.OrdinalIgnoreCase); if (!useQueryComposition) { if (relationalQueryCompilationContext.IsIncludeQuery) { throw new InvalidOperationException( RelationalStrings.StoredProcedureIncludeNotSupported); } } if (useQueryComposition && fromSqlAnnotation.QueryModel.IsIdentityQuery() && !fromSqlAnnotation.QueryModel.ResultOperators.Any() && !relationalQueryCompilationContext.IsIncludeQuery) { useQueryComposition = false; } if (!useQueryComposition) { QueryModelVisitor.RequiresClientEval = true; querySqlGeneratorFunc = () => selectExpression.CreateFromSqlQuerySqlGenerator( fromSqlAnnotation.Sql, fromSqlAnnotation.Arguments); } } var shaper = CreateShaper(elementType, entityType, selectExpression); return(Expression.Call( QueryModelVisitor.QueryCompilationContext.QueryMethodProvider // TODO: Don't use ShapedQuery when projecting .ShapedQueryMethod .MakeGenericMethod(shaper.Type), EntityQueryModelVisitor.QueryContextParameter, Expression.Constant(_shaperCommandContextFactory.Create(querySqlGeneratorFunc)), Expression.Constant(shaper))); }
protected override Expression VisitEntityQueryable(Type elementType) { if (elementType == null) { throw new ArgumentNullException(nameof(elementType)); } var relationalQueryCompilationContext = QueryModelVisitor.QueryCompilationContext; var valueFromOpenJsonAnnotation = relationalQueryCompilationContext .QueryAnnotations .OfType <ValueFromOpenJsonOperator>() .LastOrDefault(a => a.QuerySource == _querySource); if (valueFromOpenJsonAnnotation != null) { if (valueFromOpenJsonAnnotation.Json.NodeType == ExpressionType.MemberAccess && valueFromOpenJsonAnnotation.Json is MemberExpression me) { var entityType = relationalQueryCompilationContext.FindEntityType(_querySource) ?? _model.FindEntityType(elementType); var selectExpression = _selectExpressionFactory.Create(relationalQueryCompilationContext); QueryModelVisitor.AddQuery(_querySource, selectExpression); var name = entityType.Relational().TableName; var tableAlias = relationalQueryCompilationContext.CreateUniqueTableAlias( _querySource.HasGeneratedItemName() ? name[0].ToString().ToLowerInvariant() : (_querySource as GroupJoinClause)?.JoinClause.ItemName ?? _querySource.ItemName); Func <IQuerySqlGenerator> querySqlGeneratorFunc = selectExpression.CreateDefaultQuerySqlGenerator; var memberEntityType = relationalQueryCompilationContext.Model.FindEntityType(me.Expression.Type); var memberProperty = memberEntityType.FindProperty(me.Member as PropertyInfo); var expr = new ValueFromOpenJsonExpression(_querySource, valueFromOpenJsonAnnotation.Json, valueFromOpenJsonAnnotation.Path, tableAlias); expr.PropertyMapping[expr.Json] = memberProperty; //var jsonProperties = MemberAccessBindingExpressionVisitor.GetPropertyPath(expr.Json, relationalQueryCompilationContext, out var qsr); //Console.WriteLine(jsonProperties); //expr = (ValueFromOpenJsonExpression)Visit(expr); //expr.PropertyMapping[expr.Json] = memberProperty; //expr = (ValueFromOpenJsonExpression)QueryModelVisitor.ReplaceClauseReferences(expr); //expr.PropertyMapping[expr.Json] = memberProperty; selectExpression.AddTable(expr); //var trimmedSql = valueFromOpenJsonAnnotation.Sql.TrimStart('\r', '\n', '\t', ' '); var useQueryComposition = true; //= trimmedSql.StartsWith("SELECT ", StringComparison.OrdinalIgnoreCase) // || trimmedSql.StartsWith("SELECT" + Environment.NewLine, StringComparison.OrdinalIgnoreCase) // || trimmedSql.StartsWith("SELECT\t", StringComparison.OrdinalIgnoreCase); var requiresClientEval = !useQueryComposition; if (!useQueryComposition) { if (relationalQueryCompilationContext.IsIncludeQuery) { throw new InvalidOperationException( RelationalStrings.StoredProcedureIncludeNotSupported); } } if (useQueryComposition && valueFromOpenJsonAnnotation.QueryModel.IsIdentityQuery() && !valueFromOpenJsonAnnotation.QueryModel.ResultOperators.Any() && !relationalQueryCompilationContext.IsIncludeQuery) { useQueryComposition = false; } if (!useQueryComposition) { throw new NotImplementedException(); //QueryModelVisitor.RequiresClientEval = requiresClientEval; //querySqlGeneratorFunc = () // => selectExpression.CreateFromSqlQuerySqlGenerator( // fromSqlAnnotation.Sql, // fromSqlAnnotation.Arguments); } var shaper = CreateShaper(elementType, entityType, selectExpression); return(Expression.Call( QueryModelVisitor.QueryCompilationContext.QueryMethodProvider // TODO: Don't use ShapedQuery when projecting .ShapedQueryMethod .MakeGenericMethod(shaper.Type), EntityQueryModelVisitor.QueryContextParameter, Expression.Constant(_shaperCommandContextFactory.Create(querySqlGeneratorFunc)), Expression.Constant(shaper))); //var member = valueFromOpenJsonAnnotation.Arguments as MemberExpression; //if (member.Type == typeof(string)) //{ // var m = this.VisitMember(member); //} } } return(base.VisitEntityQueryable(elementType)); }