EntityFrameworkCore中的IDbAsyncQueryProvider

时间:2016-09-27 08:00:34

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

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

我需要测试一个内部在我的datacontext中对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();
}

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

1 个答案:

答案 0 :(得分:11)

我终于开始工作了。他们稍微将EntityFrameworkCore中的接口从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的测试实施。我没有尝试,我只是让它发挥作用。

请记住,DbSet上的DbContext需要标记为virtual,否则您需要在DbContext上实施一些界面包装才能使其正常运行。