c# - EntityFrameworkCore 中的 IDbAsyncQueryProvider

标签 c# .net entity-framework entity-framework-core .net-core

我正在使用 XUNIT 在点网核心应用程序中进行测试。

我需要测试一项服务,该服务在我的数据上下文中对 DbSet 进行内部异步查询。

I've seen here异步模拟 DbSet 是可能的。

我遇到的问题是 IDbAsyncQueryProvider 在我使用的 EntityframeworkCore 中似乎不可用。

我这里有错吗?有其他人让这个工作吗?

(漫长的一天,希望我只是遗漏了一些简单的东西)

编辑

在 GitHub 上询问后,我找到了这个类: https://github.com/aspnet/EntityFramework/blob/dev/src/Microsoft.EntityFrameworkCore/Query/Internal/IAsyncQueryProvider.cs

这是我到目前为止在尝试实现它时所做的:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Query.Internal;

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

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

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

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

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

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

        IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression expression)
        {
            return Task.FromResult(Execute<TResult>(expression)).ToAsyncEnumerable();
        }

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

    internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, System.Collections.Generic.IAsyncEnumerable<T>, IQueryable<T>
    {
        public TestDbAsyncEnumerable(IEnumerable<T> enumerable)
            : base(enumerable)
        { }

        public TestDbAsyncEnumerable(Expression expression)
            : base(expression)
        { }

        public IAsyncEnumerator<T> GetAsyncEnumerator()
        {
            return new TestDbAsyncEnumerable<T>(this.AsEnumerable()).ToAsyncEnumerable();
        }

        IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
        {
            return GetAsyncEnumerator();
        }

        IAsyncEnumerator<T> IAsyncEnumerable<T>.GetEnumerator()
        {
            throw new NotImplementedException();
        }

        IQueryProvider IQueryable.Provider
        {
            get { return new TestAsyncQueryProvider<T>(this); }
        }
    }
}

我现在尝试实现它并遇到了更多问题,特别是围绕这两种方法:

public IAsyncEnumerator<T> GetAsyncEnumerator()
{
    return new TestDbAsyncEnumerable<T>(this.AsEnumerable()).ToAsyncEnumerable();
}

IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
    return GetAsyncEnumerator();
}

我希望有人能指出我做错了什么的正确方向。

最佳答案

我终于让它工作了。他们将 EntityFrameworkCore 中的接口(interface)从 IDbAsyncEnumerable 稍微更改为 IAsyncEnumerable 因此以下代码对我有用:

public class AsyncEnumerable<T> : EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T>
{
    public AsyncEnumerable(Expression expression)
        : base(expression) { }

    public IAsyncEnumerator<T> GetEnumerator() =>
        new AsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}

public class AsyncEnumerator<T> : IAsyncEnumerator<T>
{
    private readonly IEnumerator<T> enumerator;

    public AsyncEnumerator(IEnumerator<T> enumerator) =>
        this.enumerator = enumerator ?? throw new ArgumentNullException();

    public T Current => enumerator.Current;

    public void Dispose() { }

    public Task<bool> MoveNext(CancellationToken cancellationToken) =>
        Task.FromResult(enumerator.MoveNext());
}

[Fact]
public async Task TestEFCore()
{
    var data =
        new List<Entity>()
        {
            new Entity(),
            new Entity(),
            new Entity()
        }.AsQueryable();

    var mockDbSet = new Mock<DbSet<Entity>>();

    mockDbSet.As<IAsyncEnumerable<Entity>>()
        .Setup(d => d.GetEnumerator())
        .Returns(new AsyncEnumerator<Entity>(data.GetEnumerator()));

    mockDbSet.As<IQueryable<Entity>>().Setup(m => m.Provider).Returns(data.Provider);
    mockDbSet.As<IQueryable<Entity>>().Setup(m => m.Expression).Returns(data.Expression);
    mockDbSet.As<IQueryable<Entity>>().Setup(m => m.ElementType).Returns(data.ElementType);
    mockDbSet.As<IQueryable<Entity>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

    var mockCtx = new Mock<SomeDbContext>();
    mockCtx.SetupGet(c => c.Entities).Returns(mockDbSet.Object);

    var entities = await mockCtx.Object.Entities.ToListAsync();

    Assert.NotNull(entities);
    Assert.Equal(3, entities.Count());
}

您甚至可以进一步清理 AsyncEnumerableAsyncEnumerator 的那些测试实现。我没有尝试,只是让它起作用了。

请记住,DbContext 上的 DbSet 需要标记为 virtual,否则您需要在 上实现一些接口(interface)包装器>DbContext 以使其正常工作。

关于c# - EntityFrameworkCore 中的 IDbAsyncQueryProvider,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39719258/

相关文章:

c# - 正确舍入财务数据

c# - WPF 组合框从 mysql 数据库填充数据

c# - Monitor.Wait 初始锁定?

c# - 我可以在 C# 中获得 XML 的具体示例吗

c# - 无法将类型 'System.Collections.Generic.List<System.Collections.Generic.IEnumerable<xxx>>' 隐式转换为 'System.Collections.Generic.List<xxx>

entity-framework - 树结构表和 Entity Framework

c# - 编译器常量、优化和性能

c# - 如何检查给定日期是否存在于月份?

entity-framework - Entity Framework 是否支持循环引用?

c# - 在监视 View 中格式化结构数据