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

如何解决测试中IQueryable未实现IAsyncQueryProvider异常问题

问题:单元测试调用EF异步方法时触发System.AggregateException异常

问题说明

编写单元测试验证「按ID获取分类」的方法时,出现System.AggregateException异常,核心原因是内存集合转换的IQueryable未实现EF异步操作所需的IAsyncQueryProvider接口。

相关代码

测试初始化代码

[SetUp]
public void Setup()
{
    var config = new MapperConfiguration(cfg =>
    {
        cfg.AddProfile<MapperInitilizer>();
    });
    _realMapper = new Mapper(config);
    _categories = new Mock<IRepository<Category>>();
    _unitOfWork = new Mock<IUnitOfWork>();
    _unitOfWork.SetupGet(work => work.Categories).Returns(_categories.Object);
    _logger = new Mock<ILogger<CategoryController>>();
    _mapper = new Mock<IMapper>();
    _categoryController = new CategoryController(_logger.Object, _mapper.Object,_unitOfWork.Object);
    category_one = new Category()
    {
        Id = 1,
        CreatedAt = new DateTime(2022, 8,8),
        Description = "music desc",
        Title = "music"
    };
  
    category_two = new Category()
    {
        Id = 2,
        CreatedAt = new DateTime(2022, 9,8),
        Description = "sport desc",
        Title = "sport"
    };
}

CategoryTestRepository的Get方法

public async Task<Category> Get(Expression<Func<Category, bool>> expression, Func<IQueryable<Category>, IIncludableQueryable<Category, object>> include = null)
{
    IQueryable<Category> query = Source.AsQueryable();
    return await query.AsNoTracking().FirstOrDefaultAsync(expression);
}

按ID获取分类的测试方法

[Test]
public void GetCategory_InputCategoryId_ReturnCategoryById()
{
    List<Category> categoryList = new List<Category> {category_one, category_two};

    var expectedResult = categoryList[0];
    
    var repo = new CategoryTestRepository(categoryList);
    var unitOfWork = new Mock<IUnitOfWork>();
    unitOfWork.SetupGet(work => work.Categories).Returns(repo);
    var categoryController = new CategoryController(_logger.Object, _realMapper, unitOfWork.Object);
    categoryController.GetCategory(1).GetAwaiter().GetResult();
    
    Assert.IsNotEmpty(repo.Source);
    Assert.AreEqual(expectedResult.Id, repo.Get(x => x.Id == 1).Result);
}

错误信息

System.AggregateException : 发生一个或多个错误。(源“IQueryable”的提供程序未实现“IAsyncQueryProvider”。只有实现“IAsyncQueryProvider”的提供程序才能用于 Entity Framework 异步操作。)
  ----> System.InvalidOperationException : 源“IQueryable”的提供程序未实现“IAsyncQueryProvider”。只有实现“IAsyncQueryProvider”的提供程序才能用于 Entity Framework 异步操作。
   在 System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   在 System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   在 System.Threading.Tasks.Task`1.get_Result()

解决方案

方案1:修改Repository的异步方法实现

List.AsQueryable()生成的EnumerableQuery<T>不支持EF的异步扩展方法,改用同步查询后包装成Task返回:

public async Task<Category> Get(Expression<Func<Category, bool>> expression, Func<IQueryable<Category>, IIncludableQueryable<Category, object>> include = null)
{
    IQueryable<Category> query = Source.AsQueryable();
    // 用同步FirstOrDefault查询,再通过Task.FromResult包装为异步返回
    return await Task.FromResult(query.AsNoTracking().FirstOrDefault(expression));
}

方案2:使用EF InMemory数据库(推荐)

用EF Core的内存数据库替代普通List,生成的IQueryable自带支持异步操作的IAsyncQueryProvider:

// 初始化内存数据库上下文
var options = new DbContextOptionsBuilder<YourDbContext>()
    .UseInMemoryDatabase(databaseName: "TestCategoryDb")
    .Options;

using var context = new YourDbContext(options);
context.Categories.AddRange(category_one, category_two);
context.SaveChanges();

// 基于上下文创建Repository
var repo = new CategoryRepository(context);

方案3:优化测试方法的异步调用方式

避免用.GetResult()或.Result阻塞线程,将测试方法改为异步:

[Test]
public async Task GetCategory_InputCategoryId_ReturnCategoryById()
{
    List<Category> categoryList = new List<Category> {category_one, category_two};

    var expectedResult = categoryList[0];
    
    var repo = new CategoryTestRepository(categoryList);
    var unitOfWork = new Mock<IUnitOfWork>();
    unitOfWork.SetupGet(work => work.Categories).Returns(repo);
    var categoryController = new CategoryController(_logger.Object, _realMapper, unitOfWork.Object);
    
    // 异步调用控制器方法
    var result = await categoryController.GetCategory(1);
    
    Assert.IsNotEmpty(repo.Source);
    // 异步获取查询结果
    var category = await repo.Get(x => x.Id == 1);
    Assert.AreEqual(expectedResult.Id, category.Id);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 04:54:10