IAsyncQueryProvider mock issue when migrated to .net core 3 adding TResult IAsyncQueryProvider
Asked Answered
c#
H

2

17

I did something similar to : How to mock an async repository with Entity Framework Core in one of my unit test project .net core 2.1. Now trying to update it to 3.0 preview and have some error with IAsyncQueryProvider.

So when I updated my project. i had some issue with my unit tests. In fact,IAsyncEnumerable switched GetEnumerator for GetAsyncEnumerator. Fixed that. Moreovere some interfaces changed and had to be implemented in my code.

My issue here its with IAsyncQueryProvider witch added TResult IAsyncQueryProvider.ExecuteAsync(Expression expression, CancellationToken cancellationToken) and i don't know what to do with this because since I updated to getAsyncEnumerator i go in this part of my code and can t make it work because i don't know how to return TResult I've tried: return Execute<TResult>(expression); return _inner.Execute<TResult>(expression); throw new NotImplementedException();(:p sorry had to)

    internal class TestAsyncQueryProvider<TEntity> : IAsyncQueryProvider
{
    private readonly IQueryProvider _inner;

    internal TestAsyncQueryProvider(IQueryProvider inner)
    {
        _inner = inner;
    }

    public IQueryable CreateQuery(Expression expression)
    {
        return new TestAsyncEnumerable<TEntity>(expression);
    }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        return new TestAsyncEnumerable<TElement>(expression);
    }

    public object Execute(Expression expression)
    {
        return _inner.Execute(expression);
    }

    public TResult Execute<TResult>(Expression expression)
    {
        return _inner.Execute<TResult>(expression);
    }

    public IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression expression)
    {
        return new TestAsyncEnumerable<TResult>(expression);
    }

    public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
    {
        return Task.FromResult(Execute<TResult>(expression));
    }

    TResult IAsyncQueryProvider.ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }

my test should be passing as it was in .net core 2.1

Harrier answered 1/8, 2019 at 18:19 Comment(3)
any updates on this? I'm experiencing the same issue...Arrearage
Did not found any solution. one of my colleague changed the code totally. Sorry.Whippoorwill
@SébastienHonorine Can you provide any detail on how your colleague changed this? I am also trying to upgrade and running into this issue mocking an IQueryable.Chism
C
28

I've found a library that has successfully upgraded this mock implementation to .NET Core 3.0: https://github.com/romantitov/MockQueryable/blob/master/src/MockQueryable/MockQueryable.EntityFrameworkCore/TestQueryProviderEfCore.cs (note that the code in the github repo has changed since the snippet below was posted).

The interface for Execute was replaced with ExecuteAsync in the IdentityServer upgrade.

public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
    var expectedResultType = typeof(TResult).GetGenericArguments()[0];
    var executionResult = typeof(IQueryProvider)
                         .GetMethod(
                              name: nameof(IQueryProvider.Execute),
                              genericParameterCount: 1,
                              types: new[] {typeof(Expression)})
                         .MakeGenericMethod(expectedResultType)
                         .Invoke(this, new[] {expression});

    return (TResult) typeof(Task).GetMethod(nameof(Task.FromResult))
                                ?.MakeGenericMethod(expectedResultType)
                                 .Invoke(null, new[] {executionResult});
}

The executionResult is the evaluation of the expression and then is wrapped in a task (with some reflection to make it generic) Task.FromResult(executionResult) and returned.

Chism answered 10/10, 2019 at 1:9 Comment(5)
This answer fixed my test, and my situation was the same as OP. Thank you!Nonintervention
This answer fixed my test, and my situation was the same as OP. Thank you!Turkmen
the link is dead :(Mellicent
I've updated the link (see answer history for the original if you want it for some reason) but the code has changed a little since this answer. I've left the code in the answer unchanged, and just updated the link to the equivalent file in the updated repo.Sibert
I used the code from the repo, worked great on my implementation. Thank you all :)Inebriate
C
4

I know this is older but a simpler way to do this is:

public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default)
{
    var t = Expression.Lambda(expression).Compile().DynamicInvoke();
    return Task.FromResult(t as dynamic);
}
Christhood answered 8/9, 2021 at 18:33 Comment(2)
Be careful using dynamic like this. It can work, but because you have bypassed compiler type checking (via as dynamic) failure due to incompatible types is now ambiguous, did the test fail because the expectation was not met, or did the test fail because we made an error in the test logic.Lamina
This works great when a result is returned, somehow when t is null I am having quite the trouble to make this work. 'The type arguments for method 'System.Threading.Tasks.Task.FromResult<TResult>(TResult)' cannot be inferred from the usage. Try specifying the type arguments explicitly.'Coolish

© 2022 - 2024 — McMap. All rights reserved.