Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IAsyncQueryProvider mock issue when migrated to .net core 3 adding TResult IAsyncQueryProvider

Tags:

c#

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

like image 939
Sébastien Honorine Avatar asked Aug 01 '19 18:08

Sébastien Honorine


2 Answers

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.

like image 200
Marty Loewenthal Avatar answered Nov 02 '22 00:11

Marty Loewenthal


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);
}
like image 24
Eric Renken Avatar answered Nov 02 '22 01:11

Eric Renken