【问题标题】:IAsyncQueryProvider mock issue when migrated to .net core 3 adding TResult IAsyncQueryProvider迁移到 .net core 3 添加 TResult IAsyncQueryProvider 时的 IAsyncQueryProvider 模拟问题
【发布时间】:2019-12-10 09:32:57
【问题描述】:

我做了类似的事情:How to mock an async repository with Entity Framework Core 在我的一个单元测试项目 .net core 2.1 中。现在尝试将其更新到 3.0 预览版,但 IAsyncQueryProvider 出现了一些错误。

所以当我更新我的项目时。我的单元测试有一些问题。实际上,IAsyncEnumerable 将 GetEnumerator 切换为 GetAsyncEnumerator。解决了这个问题。此外,一些接口发生了变化,必须在我的代码中实现。

我的问题是 IAsyncQueryProvider 女巫添加了 TResult IAsyncQueryProvider.ExecuteAsync(Expression expression, CancellationToken cancelToken) ,我不知道该怎么办,因为自从我更新到 getAsyncEnumerator 我进入我的这部分代码并且不能让它工作,因为我不知道如何返回 TResult 我试过了: 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();
    }

我的测试应该像在 .net core 2.1 中一样通过

【问题讨论】:

  • 这方面有什么更新吗?我遇到了同样的问题...
  • 没有找到任何解决方案。我的一位同事完全更改了代码。对不起。
  • @SébastienHonorine 您能否提供有关您的同事如何改变这一点的任何细节?我也在尝试升级并遇到模拟 IQueryable 的问题。

标签: c#


【解决方案1】:

我发现一个库已成功将此模拟实现升级到 .NET Core 3.0: https://github.com/romantitov/MockQueryable/blob/master/src/MockQueryable/MockQueryable.EntityFrameworkCore/TestQueryProviderEfCore.cs(请注意,自从下面的 sn-p 发布以来,github repo 中的代码已经更改)。

在 IdentityServer 升级中,Execute 的接口被替换为 ExecuteAsync

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});
}

executionResult 是对expression 的评估,然后被包装在一个任务中(通过一些反射使其通用)Task.FromResult(executionResult) 并返回。

【讨论】:

  • 这个答案修复了我的测试,我的情况和OP一样。谢谢!
  • 这个答案修复了我的测试,我的情况和OP一样。谢谢!
  • 链接失效:(
  • 我已经更新了链接(如果您出于某种原因需要,请参阅原始答案历史记录),但自此答案以来代码发生了一些变化。我没有改变答案中的代码,只是更新了更新后的 repo 中等效文件的链接。
【解决方案2】:

我知道这是旧的,但更简单的方法是:

public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default)
{
    var t = Expression.Lambda(expression).Compile().DynamicInvoke();
    return Task.FromResult(t as dynamic);
}

【讨论】:

  • 小心使用这样的动态。它可以工作,但是因为您绕过了编译器类型检查(通过as dynamic),由于类型不兼容而导致的失败现在是模棱两可的,测试失败是因为没有达到预期,还是测试失败是因为我们在测试逻辑。
猜你喜欢
  • 2020-05-30
  • 2020-01-09
  • 1970-01-01
  • 2021-12-09
  • 2020-05-28
  • 1970-01-01
  • 2012-09-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多