-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This change introduces derived types of EntityQueryable<T> to be processed as query root in core pipeline. Currently FromSql and Queryable functions generate a method call in query pipeline which would be mapped to a query root. This means that any visitor which need to detect query root, they have to have special code to handle this. With this change, any provider bringing custom query roots can inject custom query root for relevant subtree in the query and it would be processed transparently through the stack. Only during translation, once the custom query root needs to be intercepted to generate correct SQL. Converted FromSql in this PR. Will submit another PR to convert queryable functions. Custom query roots can be generated during query construction itself (before it reaches EF), such query root requires - Overriden Equals/GetHashCode methods so we differentiate them during query caching. - Optional ToString method for debugging print out. - Conversion to such custom roots in QueryFilter if needed. - Components of custom root cannot be parameterized in ParameterExtractingExpressionVisitor Part of #18923
- Loading branch information
Showing
19 changed files
with
228 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// 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; | ||
|
||
namespace Microsoft.EntityFrameworkCore.Query | ||
{ | ||
/// <summary> | ||
/// An interface to identify FromSql query roots in LINQ. | ||
/// </summary> | ||
public interface IFromSqlQueryable : IEntityQueryable | ||
{ | ||
/// <summary> | ||
/// Return Sql used to get data for this query root. | ||
/// </summary> | ||
string Sql { get; } | ||
|
||
/// <summary> | ||
/// Return arguments for the Sql. | ||
/// </summary> | ||
Expression Argument { get; } | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
src/EFCore.Relational/Query/Internal/CustomQueryableInjectingExpressionVisitor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// 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 System.Reflection; | ||
using JetBrains.Annotations; | ||
using Microsoft.EntityFrameworkCore.Metadata; | ||
|
||
namespace Microsoft.EntityFrameworkCore.Query.Internal | ||
{ | ||
public class CustomQueryableInjectingExpressionVisitor : ExpressionVisitor | ||
{ | ||
protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) | ||
{ | ||
if (methodCallExpression.Method.DeclaringType == typeof(RelationalQueryableExtensions) | ||
&& methodCallExpression.Method.Name == nameof(RelationalQueryableExtensions.FromSqlOnQueryable)) | ||
{ | ||
var sql = (string)((ConstantExpression)methodCallExpression.Arguments[1]).Value; | ||
var entityType = ((IEntityQueryable)((ConstantExpression)methodCallExpression.Arguments[0]).Value).EntityType; | ||
|
||
return CreateFromSqlQueryableExpression(entityType, sql, methodCallExpression.Arguments[2]); | ||
} | ||
|
||
return base.VisitMethodCall(methodCallExpression); | ||
} | ||
|
||
private static ConstantExpression CreateFromSqlQueryableExpression(IEntityType entityType, string sql, Expression argument) | ||
{ | ||
return Expression.Constant( | ||
_createFromSqlQueryableMethod | ||
.MakeGenericMethod(entityType.ClrType) | ||
.Invoke( | ||
null, new object[] { NullAsyncQueryProvider.Instance, entityType, sql, argument })); | ||
} | ||
|
||
private static readonly MethodInfo _createFromSqlQueryableMethod | ||
= typeof(CustomQueryableInjectingExpressionVisitor) | ||
.GetTypeInfo().GetDeclaredMethod(nameof(CreateFromSqlQueryable)); | ||
|
||
[UsedImplicitly] | ||
private static FromSqlQueryable<TResult> CreateFromSqlQueryable<TResult>( | ||
IAsyncQueryProvider entityQueryProvider, IEntityType entityType, string sql, Expression argument) | ||
=> new FromSqlQueryable<TResult>(entityQueryProvider, entityType, sql, argument); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// 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.Linq.Expressions; | ||
using JetBrains.Annotations; | ||
using Microsoft.EntityFrameworkCore.Infrastructure; | ||
using Microsoft.EntityFrameworkCore.Metadata; | ||
using Microsoft.EntityFrameworkCore.Utilities; | ||
|
||
namespace Microsoft.EntityFrameworkCore.Query.Internal | ||
{ | ||
/// <summary> | ||
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to | ||
/// the same compatibility standards as public APIs. It may be changed or removed without notice in | ||
/// any release. You should only use it directly in your code with extreme caution and knowing that | ||
/// doing so can result in application failures when updating to a new Entity Framework Core release. | ||
/// </summary> | ||
public class FromSqlQueryable<TResult> : EntityQueryable<TResult>, IFromSqlQueryable | ||
{ | ||
/// <summary> | ||
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to | ||
/// the same compatibility standards as public APIs. It may be changed or removed without notice in | ||
/// any release. You should only use it directly in your code with extreme caution and knowing that | ||
/// doing so can result in application failures when updating to a new Entity Framework Core release. | ||
/// </summary> | ||
public FromSqlQueryable( | ||
[NotNull] IAsyncQueryProvider queryProvider, [NotNull] IEntityType entityType, | ||
[NotNull] string sql, [NotNull] Expression argument) | ||
: base(queryProvider, entityType) | ||
{ | ||
Check.NotEmpty(sql, nameof(sql)); | ||
Check.NotNull(argument, nameof(argument)); | ||
|
||
Sql = sql; | ||
Argument = argument; | ||
} | ||
|
||
/// <summary> | ||
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to | ||
/// the same compatibility standards as public APIs. It may be changed or removed without notice in | ||
/// any release. You should only use it directly in your code with extreme caution and knowing that | ||
/// doing so can result in application failures when updating to a new Entity Framework Core release. | ||
/// </summary> | ||
public virtual string Sql { get; } | ||
|
||
/// <summary> | ||
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to | ||
/// the same compatibility standards as public APIs. It may be changed or removed without notice in | ||
/// any release. You should only use it directly in your code with extreme caution and knowing that | ||
/// doing so can result in application failures when updating to a new Entity Framework Core release. | ||
/// </summary> | ||
public virtual Expression Argument { get; } | ||
|
||
/// <summary> | ||
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to | ||
/// the same compatibility standards as public APIs. It may be changed or removed without notice in | ||
/// any release. You should only use it directly in your code with extreme caution and knowing that | ||
/// doing so can result in application failures when updating to a new Entity Framework Core release. | ||
/// </summary> | ||
public override string ToString() => $"{base.ToString()}.FromSql(\"{Sql}\", {Argument.Print()}"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.