EntityFrameworkCore 中的 IDbAsyncQueryProvider [英] IDbAsyncQueryProvider in EntityFrameworkCore

查看:24
本文介绍了EntityFrameworkCore 中的 IDbAsyncQueryProvider的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 XUNIT 在 dot net core 应用程序中进行测试.

I'm using XUNIT to test in a dot net core application.

我需要测试一个在我的数据上下文中对 DbSet 进行异步查询的服务.

I need to test a service that is internally making an async query on a DbSet in my datacontext.

我在这里看到 可以异步地嘲笑 DbSet.

I've seen here that mocking that DbSet asynchronously is possible.

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

The problem I'm having is that the IDbAsyncQueryProvider does not seem to be available in EntityframeworkCore, which I'm using.

我在这里不正确吗?有其他人让这个工作吗?

Am I incorrect here? Has anyone else got this working?

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

(Been a long day, hopefully I'm just missing something simple)

编辑

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

After asking on GitHub, I got point to this class: https://github.com/aspnet/EntityFramework/blob/dev/src/Microsoft.EntityFrameworkCore/Query/Internal/IAsyncQueryProvider.cs

这是我迄今为止在尝试实现这一点时所得到的:

This is what I've gotten to so far in trying to implement this:

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

我现在已经尝试实现这一点,但遇到了更多问题,特别是围绕这两种方法:

I've now tried to implement this and have run into some more issues, specifically around these two methods:

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

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

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

I'm hoping that somebody could point me in the right direction as to what I'm doing wrong.

推荐答案

我终于让这个工作了.他们将 EntityFrameworkCore 中的接口从 IDbAsyncEnumerable 稍微更改为 IAsyncEnumerable,因此以下代码对我有用:

I finally got this to work. They slightly changed the interfaces in EntityFrameworkCore from IDbAsyncEnumerable to IAsyncEnumerable so the following code worked for me:

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 的测试实现,甚至更多.我没有尝试,我只是让它工作.

You might be able to clean up those test implementations of the AsyncEnumerable and AsyncEnumerator even more. I didn't try, I just got it to work.

记住你的 DbContext 上的 DbSet 需要标记为 virtual 否则你需要在 DbContext 使其正常工作.

Remember your DbSet on your DbContext needs to be marked as virtual or else you will need to implement some interface wrapper over the DbContext to make this work properly.

这篇关于EntityFrameworkCore 中的 IDbAsyncQueryProvider的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆