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

.NET Core Mock UserManager时IAsyncQueryProvider加载失败问题求助

解决Mock UserManager时的IAsyncQueryProvider类型加载异常

这个问题我之前也碰到过,根源是你Mock的Users集合没有EF Core异步查询依赖的Microsoft.EntityFrameworkCore.Query.Internal.IAsyncQueryProvider——普通List<T>.AsQueryable()返回的查询提供器是同步的,完全不支持EF Core的异步扩展方法,所以调用SingleOrDefaultAsync时就会抛出类型加载异常。

下面给你几个可行的解决/规避方案:

方案一:使用第三方测试库(最省心)

推荐用EntityFrameworkCore.Testing.Moq这个专门为EF Core测试设计的Moq扩展库,它能自动帮你处理异步查询提供器的问题。

  1. 先安装NuGet包:
Install-Package EntityFrameworkCore.Testing.Moq
  1. 修改你的Mock代码:
var users = new List<UserIdentity>() { user1, user2, user3, user4 };
// 用AsDbSetMock()替换原来的BuildMock()
var mockUsers = users.AsQueryable().AsDbSetMock();
_userManager.Setup(x => x.Users).Returns(mockUsers.Object);

这样处理后,你的Users集合就会带有EF Core兼容的异步查询提供器,调用SingleOrDefaultAsync之类的方法就不会报错了。

方案二:手动实现异步查询提供器(无第三方依赖)

如果你不想引入额外的NuGet包,可以自己实现一个支持EF异步查询的IAsyncQueryProvider和相关辅助类,把你的列表包装成支持异步的IQueryable:

先添加这些辅助类:

public class AsyncQueryProvider<TEntity> : IAsyncQueryProvider
{
    private readonly IQueryProvider _inner;

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

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

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        return new AsyncEnumerable<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 AsyncEnumerable<TResult>(expression);
    }

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

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

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

    public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
    {
        return new AsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
    }

    IQueryProvider IQueryable.Provider => new AsyncQueryProvider<T>(this.Provider);
}

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

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

    public ValueTask DisposeAsync()
    {
        _inner.Dispose();
        return ValueTask.CompletedTask;
    }

    public ValueTask<bool> MoveNextAsync()
    {
        return ValueTask.FromResult(_inner.MoveNext());
    }

    public T Current => _inner.Current;
}

然后修改你的Mock初始化代码:

var users = new List<UserIdentity>() { user1, user2, user3, user4 };
// 用自定义的AsyncEnumerable包装列表
var asyncQueryable = new AsyncEnumerable<UserIdentity>(users.AsQueryable());
_userManager.Setup(x => x.Users).Returns(asyncQueryable);

方案三:临时规避(不推荐用于正式测试)

如果只是临时调试,你可以修改仓库的GetUserAsync方法,用同步查询代替异步,然后包装成Task返回:

public async Task<UserIdentity> GetUserAsync(Guid? userId)
{
    // 用同步的SingleOrDefault,再用Task.FromResult包装
    return await Task.FromResult(_userManager.Users.Where(x => x.Id.Equals(userId)).SingleOrDefault());
}

不过这个方法会改变业务代码的行为,不适合用于正式的单元测试,只适合临时排查问题时使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 22:52:36