You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决EF测试时IQueryable未实现IDbAsyncQueryProvider的报错

报错根因

报错核心是EF版本不兼容导致的接口不互通:业务项目依赖的System.Data.Entity(即EF6)中的异步查询扩展方法(如FirstOrDefaultAsync)要求数据源的IQueryable实现EF6专属的System.Data.Entity.Infrastructure.IDbAsyncQueryProvider接口,而测试项目中使用的EF Core内存数据库返回的IQueryable实现的是EF Core自家的异步查询提供器接口,二者完全不兼容,因此调用时会触发该报错。

解决方案(无需更换现有依赖)

推荐在测试项目中实现EF6异步接口的适配包装类,将EF Core返回的IQueryable做一层封装即可兼容EF6的异步方法调用,无侵入性且不需要调整现有生产依赖。

步骤1:实现3个适配类

直接在测试项目中新增以下3个类,不需要额外引入其他依赖:

using System.Collections;
using System.Data.Entity.Infrastructure;
using System.Linq.Expressions;

// 适配EF6的异步枚举器
internal class Ef6AsyncEnumerator<T> : IDbAsyncEnumerator<T>
{
    private readonly IEnumerator<T> _inner;

    public Ef6AsyncEnumerator(IEnumerator<T> inner)
    {
        _inner = inner;
    }

    public T Current => _inner.Current;
    object IDbAsyncEnumerator.Current => Current;

    public void Dispose() => _inner.Dispose();
    public Task<bool> MoveNextAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult(_inner.MoveNext());
    }
}

// 适配EF6的异步查询提供器
internal class Ef6AsyncQueryProvider<T> : IDbAsyncQueryProvider
{
    private readonly IQueryProvider _inner;

    public Ef6AsyncQueryProvider(IQueryProvider inner)
    {
        _inner = inner;
    }

    public IQueryable CreateQuery(Expression expression) => new Ef6AsyncQueryable<T>(expression);
    public IQueryable<TElement> CreateQuery<TElement>(Expression expression) => new Ef6AsyncQueryable<TElement>(expression);
    public object Execute(Expression expression) => _inner.Execute(expression);
    public TResult Execute<TResult>(Expression expression) => _inner.Execute<TResult>(expression);
    public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken) => Task.FromResult(Execute(expression));
    public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken) => Task.FromResult(Execute<TResult>(expression));
}

// 适配EF6的异步查询集合
internal class Ef6AsyncQueryable<T> : IQueryable<T>, IDbAsyncEnumerable<T>
{
    public IQueryable Inner { get; }
    public Type ElementType { get; }
    public Expression Expression { get; }
    public IQueryProvider Provider { get; }

    public Ef6AsyncQueryable(IQueryable inner)
    {
        Inner = inner;
        Provider = new Ef6AsyncQueryProvider<T>(inner.Provider);
        Expression = inner.Expression;
        ElementType = inner.ElementType;
    }

    public Ef6AsyncQueryable(Expression expression)
    {
        Expression = expression;
        ElementType = typeof(T);
    }

    public IEnumerator<T> GetEnumerator() => Inner.Cast<T>().GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    public IDbAsyncEnumerator<T> GetAsyncEnumerator() => new Ef6AsyncEnumerator<T>(GetEnumerator());
    IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() => GetAsyncEnumerator();
}

步骤2:调用时包装EF Core的查询源

在测试代码中,只要将EF Core返回的DbSet用上述适配类包装一层,再传入业务逻辑即可正常运行:

// 示例:对要传入仓储的DbSet做包装
var compatibleCategorySet = new Ef6AsyncQueryable<Category>(_userDBContext.Categories);
// 后续调用UpdateCategoryAsync时使用包装后的集合即可,不需要修改原有业务代码

如果是单元测试不需要真实执行查询,也可以直接用Mock框架模拟FirstOrDefaultAsync的返回结果,跳过真实查询逻辑,实现成本更低。

内容的提问来源于stack exchange,提问作者Daniel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 00:45:04