/// <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))); }
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))); }
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))); }
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)); }