Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query: Introduce custom query root for TVF #20165

Merged
merged 1 commit into from
Mar 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/EFCore.Cosmos/Query/Internal/SqlFunctionExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public override void Print(ExpressionPrinter expressionPrinter)

expressionPrinter.Append(Name);
expressionPrinter.Append("(");
expressionPrinter.VisitList(Arguments);
expressionPrinter.VisitCollection(Arguments);
expressionPrinter.Append(")");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,6 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
TryAdd<IQueryTranslationPreprocessorFactory, RelationalQueryTranslationPreprocessorFactory>();
TryAdd<IRelationalParameterBasedQueryTranslationPostprocessorFactory, RelationalParameterBasedQueryTranslationPostprocessorFactory>();
TryAdd<IRelationalQueryStringFactory, RelationalQueryStringFactory>();
TryAdd<INavigationExpandingExpressionVisitorFactory, RelationalNavigationExpandingExpressionVisitorFactory>();

ServiceCollectionMap.GetInfrastructure()
.AddDependencySingleton<RelationalSqlGenerationHelperDependencies>()
Expand Down
18 changes: 2 additions & 16 deletions src/EFCore.Relational/Properties/RelationalStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions src/EFCore.Relational/Properties/RelationalStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -423,12 +423,6 @@
<data name="DbFunctionInvalidInstanceType" xml:space="preserve">
<value>The DbFunction '{function}' defined on type '{type}' must be either a static method or an instance method defined on a DbContext subclass. Instance methods on other types are not supported.</value>
</data>
<data name="DbFunctionCantProjectIQueryable" xml:space="preserve">
<value>Queryable Db Functions used in projections cannot return IQueryable. IQueryable must be converted to a collection type such as List or Array.</value>
</data>
<data name="DbFunctionProjectedCollectionMustHavePK" xml:space="preserve">
<value>Return type of a queryable function '{functionName}' which is used in a projected collection must define a primary key.</value>
</data>
<data name="ConflictingAmbientTransaction" xml:space="preserve">
<value>An ambient transaction has been detected. The ambient transaction needs to be completed before beginning a transaction on this connection.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Query.Internal
{
public class QueryableFunctionQueryRootExpression : QueryRootExpression
{
//Since this is always generated while compiling there is no query provider associated
public QueryableFunctionQueryRootExpression(
[NotNull] IEntityType entityType, [NotNull] IDbFunction function, [NotNull] IReadOnlyCollection<Expression> arguments)
: base(entityType)
{
Check.NotNull(function, nameof(function));
Check.NotNull(arguments, nameof(arguments));

Function = function;
Arguments = arguments;
}

public virtual IDbFunction Function { get; }
public virtual IReadOnlyCollection<Expression> Arguments { get; }

protected override Expression VisitChildren(ExpressionVisitor visitor)
{
var arguments = new List<Expression>();
var changed = false;
foreach (var argument in Arguments)
{
var newArgument = visitor.Visit(argument);
arguments.Add(newArgument);
changed |= argument != newArgument;
}

return changed
? new QueryableFunctionQueryRootExpression(EntityType, Function, arguments)
: this;
}

public override void Print(ExpressionPrinter expressionPrinter)
{
expressionPrinter.Append(Function.MethodInfo.DisplayName());
expressionPrinter.Append("(");
expressionPrinter.VisitCollection(Arguments);
expressionPrinter.Append(")");
}

public override bool Equals(object obj)
=> obj != null
&& (ReferenceEquals(this, obj)
|| obj is QueryableFunctionQueryRootExpression queryRootExpression
&& Equals(queryRootExpression));

private bool Equals(QueryableFunctionQueryRootExpression queryRootExpression)
=> base.Equals(queryRootExpression)
&& Equals(Function, queryRootExpression.Function)
&& Arguments.SequenceEqual(queryRootExpression.Arguments, ExpressionEqualityComparer.Instance);

public override int GetHashCode()
{
var hashCode = new HashCode();
hashCode.Add(base.GetHashCode());
hashCode.Add(Function);
foreach (var item in Arguments)
{
hashCode.Add(item);
}

return hashCode.ToHashCode();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Linq.Expressions;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Query.Internal
{
public class QueryableFunctionToQueryRootConvertingExpressionVisitor : ExpressionVisitor
{
private readonly IModel _model;

public QueryableFunctionToQueryRootConvertingExpressionVisitor([NotNull] IModel model)
{
Check.NotNull(model, nameof(model));

_model = model;
}

protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
var function = _model.FindDbFunction(methodCallExpression.Method);

return function?.IsIQueryable == true
? CreateQueryableFunctionQueryRootExpression(function, methodCallExpression.Arguments)
: base.VisitMethodCall(methodCallExpression);
}

private Expression CreateQueryableFunctionQueryRootExpression(
IDbFunction function, IReadOnlyCollection<Expression> arguments)
{
var entityType = _model.FindEntityType(function.MethodInfo.ReturnType.GetGenericArguments()[0]);

return new QueryableFunctionQueryRootExpression(entityType, function, arguments);
}
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -246,11 +246,6 @@ protected override Expression VisitNew(NewExpression newExpression)
return null;
}

if (newExpression.Arguments.Any(arg => arg is MethodCallExpression methodCallExp && _model.FindDbFunction(methodCallExp.Method)?.IsIQueryable == true))
{
throw new InvalidOperationException(RelationalStrings.DbFunctionCantProjectIQueryable());
}

var newArguments = new Expression[newExpression.Arguments.Count];
for (var i = 0; i < newArguments.Length; i++)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,20 @@ public class RelationalQueryTranslationPreprocessorFactory : IQueryTranslationPr
{
private readonly QueryTranslationPreprocessorDependencies _dependencies;
private readonly RelationalQueryTranslationPreprocessorDependencies _relationalDependencies;
private readonly INavigationExpandingExpressionVisitorFactory _navigationExpandingExpressionVisitorFactory;

public RelationalQueryTranslationPreprocessorFactory(
[NotNull] QueryTranslationPreprocessorDependencies dependencies,
[NotNull] RelationalQueryTranslationPreprocessorDependencies relationalDependencies,
[NotNull] INavigationExpandingExpressionVisitorFactory navigationExpandingExpressionVisitorFactory)
[NotNull] RelationalQueryTranslationPreprocessorDependencies relationalDependencies)
{
_dependencies = dependencies;
_relationalDependencies = relationalDependencies;
_navigationExpandingExpressionVisitorFactory = navigationExpandingExpressionVisitorFactory;
}

public virtual QueryTranslationPreprocessor Create(QueryCompilationContext queryCompilationContext)
{
Check.NotNull(queryCompilationContext, nameof(queryCompilationContext));

return new RelationalQueryTranslationPreprocessor(_dependencies, _relationalDependencies, queryCompilationContext, _navigationExpandingExpressionVisitorFactory);
return new RelationalQueryTranslationPreprocessor(_dependencies, _relationalDependencies, queryCompilationContext);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ public RelationalEvaluatableExpressionFilter(
/// </summary>
protected virtual RelationalEvaluatableExpressionFilterDependencies RelationalDependencies { get; }

/// <summary>
/// Checks whether the given expression can be evaluated.
/// </summary>
/// <param name="expression"> The expression. </param>
/// <param name="model"> The model. </param>
/// <summary>
/// Checks whether the given expression can be evaluated.
/// </summary>
/// <param name="expression"> The expression. </param>
/// <param name="model"> The model. </param>
/// <returns> True if the expression can be evaluated; false otherwise. </returns>
public override bool IsEvaluatableExpression(Expression expression, IModel model)
{
Expand All @@ -67,7 +67,7 @@ public override bool IsEvaluatableExpression(Expression expression, IModel model
return base.IsEvaluatableExpression(expression, model);
}

public override bool IsQueryableFunction(Expression expression, IModel model) =>
public override bool IsQueryableFunction(Expression expression, IModel model) =>
expression is MethodCallExpression methodCallExpression
&& model.FindDbFunction(methodCallExpression.Method)?.IsIQueryable == true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Linq.Expressions;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Query
Expand All @@ -11,15 +13,22 @@ public class RelationalQueryTranslationPreprocessor : QueryTranslationPreprocess
public RelationalQueryTranslationPreprocessor(
[NotNull] QueryTranslationPreprocessorDependencies dependencies,
[NotNull] RelationalQueryTranslationPreprocessorDependencies relationalDependencies,
[NotNull] QueryCompilationContext queryCompilationContext,
[NotNull] INavigationExpandingExpressionVisitorFactory navigationExpandingExpressionVisitorFactory)
: base(dependencies, queryCompilationContext, navigationExpandingExpressionVisitorFactory)
[NotNull] QueryCompilationContext queryCompilationContext)
: base(dependencies, queryCompilationContext)
{
Check.NotNull(relationalDependencies, nameof(relationalDependencies));

RelationalDependencies = relationalDependencies;
}

protected virtual RelationalQueryTranslationPreprocessorDependencies RelationalDependencies { get; }

public override Expression NormalizeQueryableMethodCall(Expression expression)
{
expression = base.NormalizeQueryableMethodCall(expression);
expression = new QueryableFunctionToQueryRootConvertingExpressionVisitor(QueryCompilationContext.Model).Visit(expression);

return expression;
}
}
}
Loading