Skip to content

Commit

Permalink
Query: Remove ReLinq dependency from EntityQueryable (#13722)
Browse files Browse the repository at this point in the history
Part of #12048
  • Loading branch information
smitpatel authored Oct 23, 2018
1 parent cabf31b commit 913d49d
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ public object Execute(Expression expression)
throw new NotImplementedException();
}

public IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression expression)
public TResult ExecuteAsync<TResult>(Expression expression)
{
throw new NotImplementedException();
}
Expand Down
3 changes: 2 additions & 1 deletion src/EFCore/Query/Internal/CompiledAsyncEnumerableQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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.Expressions;
using JetBrains.Annotations;

Expand Down Expand Up @@ -93,7 +94,7 @@ public virtual AsyncEnumerable<TResult> Execute<TParam1, TParam2, TParam3, TPara
protected override Func<QueryContext, AsyncEnumerable<TResult>> CreateCompiledQuery(
IQueryCompiler queryCompiler, Expression expression)
{
var compiledQuery = queryCompiler.CreateCompiledAsyncEnumerableQuery<TResult>(expression);
var compiledQuery = queryCompiler.CreateCompiledAsyncQuery<IAsyncEnumerable<TResult>>(expression);

return qc => new AsyncEnumerable<TResult>(compiledQuery(qc));
}
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore/Query/Internal/CompiledAsyncTaskQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,6 @@ public virtual Task<TResult> ExecuteAsync<TParam1, TParam2, TParam3, TParam4, TP
/// </summary>
protected override Func<QueryContext, Task<TResult>> CreateCompiledQuery(
IQueryCompiler queryCompiler, Expression expression)
=> queryCompiler.CreateCompiledAsyncTaskQuery<TResult>(expression);
=> queryCompiler.CreateCompiledAsyncSingletonQuery<TResult>(expression);
}
}
4 changes: 2 additions & 2 deletions src/EFCore/Query/Internal/CompiledQueryCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ public virtual Func<QueryContext, TResult> GetOrAddQuery<TResult>(
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual Func<QueryContext, IAsyncEnumerable<TResult>> GetOrAddAsyncQuery<TResult>(
object cacheKey, Func<Func<QueryContext, IAsyncEnumerable<TResult>>> compiler)
public virtual Func<QueryContext, TResult> GetOrAddAsyncQuery<TResult>(
object cacheKey, Func<Func<QueryContext, TResult>> compiler)
=> GetOrAddQueryCore(cacheKey, compiler);

private Func<QueryContext, TFunc> GetOrAddQueryCore<TFunc>(
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore/Query/Internal/EntityQueryProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public virtual object Execute(Expression expression)
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression expression)
public virtual TResult ExecuteAsync<TResult>(Expression expression)
=> _queryCompiler.ExecuteAsync<TResult>(expression);

/// <summary>
Expand Down
61 changes: 52 additions & 9 deletions src/EFCore/Query/Internal/EntityQueryable`.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,92 @@
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Utilities;
using Remotion.Linq;

namespace Microsoft.EntityFrameworkCore.Query.Internal
{
/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public class EntityQueryable<TResult> : QueryableBase<TResult>, IAsyncEnumerable<TResult>, IDetachableContext, IListSource
public class EntityQueryable<TResult>
: IOrderedQueryable<TResult>,
IAsyncEnumerable<TResult>,
IDetachableContext,
IListSource
{
private static readonly EntityQueryable<TResult> _detached
= new EntityQueryable<TResult>(NullAsyncQueryProvider.Instance);

private readonly IAsyncQueryProvider _queryProvider;

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public EntityQueryable([NotNull] IAsyncQueryProvider provider)
: base(Check.NotNull(provider, nameof(provider)))
public EntityQueryable([NotNull] IAsyncQueryProvider queryProvider)
{
Check.NotNull(queryProvider, nameof(queryProvider));

_queryProvider = queryProvider;
Expression = Expression.Constant(this);
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public EntityQueryable([NotNull] IAsyncQueryProvider provider, [NotNull] Expression expression)
: base(
Check.NotNull(provider, nameof(provider)),
Check.NotNull(expression, nameof(expression)))
public EntityQueryable([NotNull] IAsyncQueryProvider queryProvider, [NotNull] Expression expression)
{
Check.NotNull(queryProvider, nameof(queryProvider));
Check.NotNull(expression, nameof(expression));

_queryProvider = queryProvider;
Expression = expression;
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual Type ElementType => typeof(TResult);

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual Expression Expression { get; }

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual IQueryProvider Provider => _queryProvider;

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual IEnumerator<TResult> GetEnumerator()
=> _queryProvider.Execute<IEnumerable<TResult>>(Expression).GetEnumerator();

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
IEnumerator IEnumerable.GetEnumerator()
=> _queryProvider.Execute<IEnumerable>(Expression).GetEnumerator();

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
IAsyncEnumerator<TResult> IAsyncEnumerable<TResult>.GetEnumerator()
=> ((IAsyncQueryProvider)Provider).ExecuteAsync<TResult>(Expression).GetEnumerator();
=> _queryProvider.ExecuteAsync<IAsyncEnumerable<TResult>>(Expression).GetEnumerator();

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore/Query/Internal/IAsyncQueryProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public interface IAsyncQueryProvider : IQueryProvider
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
IAsyncEnumerable<TResult> ExecuteAsync<TResult>([NotNull] Expression expression);
TResult ExecuteAsync<TResult>([NotNull] Expression expression);

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
Expand Down
4 changes: 2 additions & 2 deletions src/EFCore/Query/Internal/ICompiledQueryCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ Func<QueryContext, TResult> GetOrAddQuery<TResult>(
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
Func<QueryContext, IAsyncEnumerable<TResult>> GetOrAddAsyncQuery<TResult>(
Func<QueryContext, TResult> GetOrAddAsyncQuery<TResult>(
[NotNull] object cacheKey,
[NotNull] Func<Func<QueryContext, IAsyncEnumerable<TResult>>> compiler);
[NotNull] Func<Func<QueryContext, TResult>> compiler);
}
}
6 changes: 3 additions & 3 deletions src/EFCore/Query/Internal/IQueryCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public interface IQueryCompiler
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
IAsyncEnumerable<TResult> ExecuteAsync<TResult>([NotNull] Expression query);
TResult ExecuteAsync<TResult>([NotNull] Expression query);

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
Expand All @@ -44,12 +44,12 @@ public interface IQueryCompiler
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
Func<QueryContext, IAsyncEnumerable<TResult>> CreateCompiledAsyncEnumerableQuery<TResult>([NotNull] Expression query);
Func<QueryContext, TResult> CreateCompiledAsyncQuery<TResult>([NotNull] Expression query);

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
Func<QueryContext, Task<TResult>> CreateCompiledAsyncTaskQuery<TResult>([NotNull] Expression query);
Func<QueryContext, Task<TResult>> CreateCompiledAsyncSingletonQuery<TResult>([NotNull] Expression query);
}
}
6 changes: 3 additions & 3 deletions src/EFCore/Query/Internal/NullAsyncQueryProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,17 @@ object IQueryProvider.Execute(Expression expression)
throw new NotImplementedException();
}

TResult1 IQueryProvider.Execute<TResult1>(Expression expression)
TResult IQueryProvider.Execute<TResult>(Expression expression)
{
throw new NotImplementedException();
}

IAsyncEnumerable<TResult1> IAsyncQueryProvider.ExecuteAsync<TResult1>(Expression expression)
TResult IAsyncQueryProvider.ExecuteAsync<TResult>(Expression expression)
{
throw new NotImplementedException();
}

Task<TResult1> IAsyncQueryProvider.ExecuteAsync<TResult1>(Expression expression, CancellationToken cancellationToken)
Task<TResult> IAsyncQueryProvider.ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
Expand Down
87 changes: 48 additions & 39 deletions src/EFCore/Query/Internal/QueryCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public class QueryCompiler : IQueryCompiler
= typeof(IDatabase).GetTypeInfo()
.GetDeclaredMethod(nameof(IDatabase.CompileQuery));

private static MethodInfo CompileAsyncQueryMethod { get; }
= typeof(IDatabase).GetTypeInfo()
.GetDeclaredMethod(nameof(IDatabase.CompileAsyncQuery));

private readonly IQueryContextFactory _queryContextFactory;
private readonly ICompiledQueryCache _compiledQueryCache;
private readonly ICompiledQueryCacheKeyGenerator _compiledQueryCacheKeyGenerator;
Expand Down Expand Up @@ -158,23 +162,28 @@ var resultItemType
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression query)
public virtual TResult ExecuteAsync<TResult>(Expression query)
{
Check.NotNull(query, nameof(query));

var queryContext = _queryContextFactory.Create();

query = _queryModelGenerator.ExtractParameters(_logger, query, queryContext);

return CompileAsyncQuery<TResult>(query)(queryContext);
var compiledQuery
= _compiledQueryCache
.GetOrAddAsyncQuery(
_compiledQueryCacheKeyGenerator.GenerateCacheKey(query, async: true),
() => CompileAsyncQueryCore<TResult>(query, _queryModelGenerator, _database));

return compiledQuery(queryContext);
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual Func<QueryContext, IAsyncEnumerable<TResult>> CreateCompiledAsyncEnumerableQuery<TResult>(
Expression query)
public virtual Func<QueryContext, TResult> CreateCompiledAsyncQuery<TResult>(Expression query)
{
Check.NotNull(query, nameof(query));

Expand All @@ -188,40 +197,39 @@ public virtual Func<QueryContext, IAsyncEnumerable<TResult>> CreateCompiledAsync
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual Func<QueryContext, Task<TResult>> CreateCompiledAsyncTaskQuery<TResult>(Expression query)
public virtual Task<TResult> ExecuteAsync<TResult>(Expression query, CancellationToken cancellationToken)
{
Check.NotNull(query, nameof(query));

query = _queryModelGenerator.ExtractParameters(
_logger, query, _queryContextFactory.Create(), parameterize: false);
var queryContext = _queryContextFactory.Create();

var compiledQuery = CompileAsyncQueryCore<TResult>(query, _queryModelGenerator, _database);
queryContext.CancellationToken = cancellationToken;

return CreateCompiledSingletonAsyncQuery(compiledQuery, _logger, _contextType);
}
query = _queryModelGenerator.ExtractParameters(_logger, query, queryContext);

private static Func<QueryContext, Task<TResult>> CreateCompiledSingletonAsyncQuery<TResult>(
Func<QueryContext, IAsyncEnumerable<TResult>> compiledQuery,
IDiagnosticsLogger<DbLoggerCategory.Query> logger, Type contextType)
=> qc => ExecuteSingletonAsyncQuery(qc, compiledQuery, logger, contextType);
var compiledQuery
= _compiledQueryCache
.GetOrAddAsyncQuery(
_compiledQueryCacheKeyGenerator.GenerateCacheKey(query, async: true),
() => CompileAsyncQueryCore<IAsyncEnumerable<TResult>>(query, _queryModelGenerator, _database));

return ExecuteSingletonAsyncQuery(queryContext, compiledQuery, _logger, _contextType);
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual Task<TResult> ExecuteAsync<TResult>(Expression query, CancellationToken cancellationToken)
public virtual Func<QueryContext, Task<TResult>> CreateCompiledAsyncSingletonQuery<TResult>(Expression query)
{
Check.NotNull(query, nameof(query));

var queryContext = _queryContextFactory.Create();

queryContext.CancellationToken = cancellationToken;

query = _queryModelGenerator.ExtractParameters(_logger, query, queryContext);
query = _queryModelGenerator.ExtractParameters(
_logger, query, _queryContextFactory.Create(), parameterize: false);

var compiledQuery = CompileAsyncQuery<TResult>(query);
var compiledQuery = CompileAsyncQueryCore<IAsyncEnumerable<TResult>>(query, _queryModelGenerator, _database);

return ExecuteSingletonAsyncQuery(queryContext, compiledQuery, _logger, _contextType);
return qc => ExecuteSingletonAsyncQuery(qc, compiledQuery, _logger, _contextType);
}

private static async Task<TResult> ExecuteSingletonAsyncQuery<TResult>(
Expand Down Expand Up @@ -249,29 +257,30 @@ private static async Task<TResult> ExecuteSingletonAsyncQuery<TResult>(
}
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
protected virtual Func<QueryContext, IAsyncEnumerable<TResult>> CompileAsyncQuery<TResult>(
[NotNull] Expression query)
{
Check.NotNull(query, nameof(query));

return _compiledQueryCache
.GetOrAddAsyncQuery(
_compiledQueryCacheKeyGenerator.GenerateCacheKey(query, async: true),
() => CompileAsyncQueryCore<TResult>(query, _queryModelGenerator, _database));
}

private static Func<QueryContext, IAsyncEnumerable<TResult>> CompileAsyncQueryCore<TResult>(
private static Func<QueryContext, TResult> CompileAsyncQueryCore<TResult>(
Expression query,
IQueryModelGenerator queryModelGenerator,
IDatabase database)
{
var queryModel = queryModelGenerator.ParseQuery(query);

return database.CompileAsyncQuery<TResult>(queryModel);
var resultItemType
= (queryModel.GetOutputDataInfo()
as StreamedSequenceInfo)?.ResultItemType
?? typeof(TResult).TryGetSequenceType();

try
{
return (Func<QueryContext, TResult>)CompileAsyncQueryMethod
.MakeGenericMethod(resultItemType)
.Invoke(database, new object[] { queryModel });
}
catch (TargetInvocationException e)
{
ExceptionDispatchInfo.Capture(e.InnerException).Throw();

throw;
}
}
}
}
2 changes: 1 addition & 1 deletion test/EFCore.Tests/Extensions/QueryableExtensionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ var cancellationTokenPresent
public IQueryable<TElement> CreateQuery<TElement>(Expression expression) => throw new NotImplementedException();
public object Execute(Expression expression) => throw new NotImplementedException();
public TResult Execute<TResult>(Expression expression) => throw new NotImplementedException();
public IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression expression) => throw new NotImplementedException();
public TResult ExecuteAsync<TResult>(Expression expression) => throw new NotImplementedException();
}

private class FakeQueryable<TElement> : IQueryable<TElement>
Expand Down

0 comments on commit 913d49d

Please sign in to comment.