使用 MockQueryable 时,源 'IQueryable' 的提供程序未实现 'IAsyncQueryProvider'

The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider' when using MockQueryable

我正在使用 .NET 6,我正在尝试模拟 IQueryable return 以使用 MockQueryable 库实现 IAsyncQueryProvider,但是我在使用和不使用该库时都面临着同样的错误:

 Message:  System.InvalidOperationException : The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'. Only providers that implement 'IAsyncQueryProvider' can be used for Entity Framework asynchronous operations.

Stack Trace:  EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable1 source, Expression expression, CancellationToken cancellationToken) EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable1 source, CancellationToken cancellationToken) EntityFrameworkQueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken) UserService.GetExistingUser(String externalId, CancellationToken cancellationToken) line 81 UserService.CreateAsync(UserDto dto, CancellationToken cancellationToken) line 21 UserServiceTests.CreateAsync_ShouldCreateUser_WhenUserDoesNotExist() line 46 --- End of stack trace from previous location ---

也许有人可以帮我找出可能是明显错误的地方?

这是我的单元测试class:

public class UserServiceTests
{
    private readonly UserService _sut;
    private readonly Mock<IRepository<User>> _userRepositoryMock = new();

    public UserServiceTests()
    {
        _sut = new UserService(_userRepositoryMock.Object);
    }

    [Fact]
    public async Task CreateAsync_ShouldCreateUser_WhenUserDoesNotExist()
    {
        // Arrange
        var dto = new UserDto
        {
            EmailAddress = "createAsyncTest@email.com",
            ExternalId = "externalId2",
            Name = "Create Async"
        };

        var users = new List<User>();

        var mock = users.BuildMock();

        _userRepositoryMock.Setup(x => x.Find(y => y.ExternalId == dto.ExternalId)).Returns(mock).Verifiable();

        var user = UserMapper.GetUserExpression(null).Compile().Invoke(dto);

        _userRepositoryMock.Setup(x => x.InsertAsync(user, true, default)).Verifiable();

        // Act
        var res = await _sut.CreateAsync(dto);

        // Assert 
        Assert.NotNull(res);
        _userRepositoryMock.Verify();
    }
}

这就是我实现通用存储库的方式:

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    internal TestContext _context;
    internal DbSet<TEntity> _dbSet;

    public Repository(TestContext context)
    {
        _context = context;
        _context.Database.SetCommandTimeout(300);
        _dbSet = context.Set<TEntity>();
    }

    public IQueryable<TEntity> All() => _dbSet;

    public IQueryable<TEntity> Find() => All();

    public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
    {
        return _dbSet.Where(predicate);
    }
}

这是我的用户服务:

public class UserService : IUserService
{
    private readonly IRepository<User> _userRepository;

    public UserService(IRepository<User> userRepository)
    {
        _userRepository = userRepository;
    }

    public async Task<UserDto> CreateAsync(UserDto dto, CancellationToken cancellationToken = default)
    {
        var existingUser = await GetExistingUser(dto.ExternalId, cancellationToken);

        if (existingUser != null)
            throw new Exception("User already exists!");

        var user = UserMapper.GetUserExpression(null).Compile().Invoke(dto);

        await _userRepository.InsertAsync(user, true, cancellationToken);

        return UserMapper.GetUserDtoExpression().Compile().Invoke(user);
    }
    
    private async Task<User> GetExistingUser(string externalId, CancellationToken cancellationToken = default)
    {
        return await _userRepository
            .Find(x => x.ExternalId == externalId)
            .FirstOrDefaultAsync(cancellationToken);
    }
}

传递给 _userRepository.Find() 的 Expression<Func<User, bool>> 类型的谓词与您在 _userRepositoryMock.Setup() 中指定的表达式不同(它们具有相同的逻辑和相同的 externalId,但它们是不同的实例).

而不是

_userRepositoryMock.Setup(x => x.Find(y => y.ExternalId == dto.ExternalId)).Returns(mock)

试试这个:

_userRepositoryMock.Setup(x => x.Find(It.IsAny<Expression<Func<User, bool>>>()).Returns(predicate => mock.Where(predicate))

这样,传递给 .Find() 方法的任何表达式都将按原样应用于模拟存储库。