-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix IAsyncQueryProvider implementation (#2)
* Fix IAsyncQueryProvider implementation
- Loading branch information
1 parent
841781e
commit 109e0ce
Showing
6 changed files
with
159 additions
and
44 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
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
82 changes: 40 additions & 42 deletions
82
Telerik.JustMock.EntityFrameworkCore/TestDbAsyncQueryProvider.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 |
---|---|---|
@@ -1,43 +1,41 @@ | ||
using Microsoft.EntityFrameworkCore.Query; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Telerik.JustMock.EntityFrameworkCore | ||
{ | ||
internal class TestDbAsyncQueryProvider<TEntity> : IAsyncQueryProvider | ||
{ | ||
private readonly IQueryProvider _inner; | ||
|
||
public TestDbAsyncQueryProvider(IQueryProvider inner) | ||
{ | ||
_inner = inner; | ||
} | ||
|
||
public IQueryable CreateQuery(Expression expression) | ||
{ | ||
return new TestDbAsyncEnumerable<TEntity>(expression); | ||
} | ||
|
||
public IQueryable<TElement> CreateQuery<TElement>(Expression expression) | ||
{ | ||
return new TestDbAsyncEnumerable<TElement>(expression); | ||
} | ||
|
||
public object Execute(Expression expression) | ||
{ | ||
return _inner.Execute(expression); | ||
} | ||
|
||
public TResult Execute<TResult>(Expression expression) | ||
{ | ||
return _inner.Execute<TResult>(expression); | ||
} | ||
|
||
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default) | ||
{ | ||
return Task.FromResult(Execute<TResult>(expression)).GetAwaiter().GetResult(); | ||
} | ||
} | ||
using Microsoft.EntityFrameworkCore.Query; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Telerik.JustMock.EntityFrameworkCore | ||
{ | ||
internal class TestDbAsyncQueryProvider<TEntity> : TestQueryProvider<TEntity>, IAsyncEnumerable<TEntity>, IAsyncQueryProvider | ||
{ | ||
public TestDbAsyncQueryProvider(Expression expression) | ||
: base(expression) | ||
{ | ||
} | ||
|
||
public TestDbAsyncQueryProvider(IEnumerable<TEntity> enumerable) | ||
: base(enumerable) | ||
{ | ||
} | ||
|
||
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken) | ||
{ | ||
var expectedResultType = typeof(TResult).GetGenericArguments()[0]; | ||
var executionResult = typeof(IQueryProvider) | ||
.GetMethods() | ||
.Single(method => method.Name == nameof(IQueryProvider.Execute) && method.IsGenericMethod) | ||
.MakeGenericMethod(expectedResultType) | ||
.Invoke(this, new object[] { expression }); | ||
|
||
return (TResult)typeof(Task).GetMethod(nameof(Task.FromResult)) | ||
.MakeGenericMethod(expectedResultType) | ||
.Invoke(null, new[] { executionResult }); | ||
} | ||
|
||
public IAsyncEnumerator<TEntity> GetAsyncEnumerator(CancellationToken cancellationToken = default) | ||
{ | ||
return new TestDbAsyncEnumerator<TEntity>(this.AsEnumerable().GetEnumerator()); | ||
} | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
Telerik.JustMock.EntityFrameworkCore/TestExpressionVisitor.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,6 @@ | ||
using System.Linq.Expressions; | ||
|
||
namespace Telerik.JustMock.EntityFrameworkCore | ||
{ | ||
internal class TestExpressionVisitor : ExpressionVisitor { } | ||
} |
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,94 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
|
||
namespace Telerik.JustMock.EntityFrameworkCore | ||
{ | ||
internal abstract class TestQueryProvider<TEntity> : IOrderedQueryable<TEntity>, IQueryProvider | ||
{ | ||
private IEnumerable<TEntity> _enumerable; | ||
|
||
protected TestQueryProvider(Expression expression) | ||
{ | ||
this.Expression = expression; | ||
} | ||
|
||
protected TestQueryProvider(IEnumerable<TEntity> enumerable) | ||
{ | ||
_enumerable = enumerable; | ||
this.Expression = enumerable.AsQueryable().Expression; | ||
} | ||
|
||
public IQueryable CreateQuery(Expression expression) | ||
{ | ||
if (expression is MethodCallExpression m) | ||
{ | ||
var resultType = m.Method.ReturnType; | ||
var tElement = resultType.GetGenericArguments().First(); | ||
return CreateIQueryableInstance<IQueryable>(tElement, expression); | ||
} | ||
|
||
return CreateQuery<TEntity>(expression); | ||
} | ||
|
||
public IQueryable<TEntity> CreateQuery<TEntity>(Expression expression) | ||
{ | ||
return CreateIQueryableInstance<IQueryable<TEntity>>(typeof(TEntity), expression); | ||
} | ||
|
||
private TQueryable CreateIQueryableInstance<TQueryable>(Type elementType, Expression expression) where TQueryable : IQueryable | ||
{ | ||
var queryType = this.GetType().GetGenericTypeDefinition().MakeGenericType(elementType); | ||
return (TQueryable)Activator.CreateInstance(queryType, expression); | ||
} | ||
|
||
public object Execute(Expression expression) | ||
{ | ||
return CompileExpressionItem<object>(expression); | ||
} | ||
|
||
public TResult Execute<TResult>(Expression expression) | ||
{ | ||
return CompileExpressionItem<TResult>(expression); | ||
} | ||
|
||
IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator() | ||
{ | ||
if (_enumerable == null) | ||
{ | ||
_enumerable = CompileExpressionItem<IEnumerable<TEntity>>(this.Expression); | ||
} | ||
|
||
return _enumerable.GetEnumerator(); | ||
} | ||
|
||
IEnumerator IEnumerable.GetEnumerator() | ||
{ | ||
if (_enumerable == null) | ||
{ | ||
_enumerable = CompileExpressionItem<IEnumerable<TEntity>>(this.Expression); | ||
} | ||
|
||
return _enumerable.GetEnumerator(); | ||
} | ||
|
||
public Type ElementType => typeof(TEntity); | ||
|
||
public Expression Expression { get; } | ||
|
||
public IQueryProvider Provider | ||
{ | ||
get { return this; } | ||
} | ||
|
||
private static TResult CompileExpressionItem<TResult>(Expression expression) | ||
{ | ||
var visitor = new TestExpressionVisitor(); | ||
var body = visitor.Visit(expression); | ||
var f = Expression.Lambda<Func<TResult>>(body ?? throw new InvalidOperationException($"{nameof(body)} is null"), (IEnumerable<ParameterExpression>)null); | ||
return f.Compile()(); | ||
} | ||
} | ||
} |