The provider for the source IQueryable doesn't implement IAsyncQueryProvider

I have some codes like below, I want to write unit tests my method. But I'm stuck in async methods. Can you help me please ?

public class Panel { public int Id { get; set; } [Required] public double Latitude { get; set; } public double Longitude { get; set; } [Required] public string Serial { get; set; } public string Brand { get; set; } } public class CrossSolarDbContext : DbContext { public CrossSolarDbContext() { } public CrossSolarDbContext(DbContextOptions<CrossSolarDbContext> options) : base(options) { } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { } } public interface IGenericRepository<T> { Task<T> GetAsync(string id); IQueryable<T> Query(); Task InsertAsync(T entity); Task UpdateAsync(T entity); } public abstract class GenericRepository<T> : IGenericRepository<T> where T : class, new() { protected CrossSolarDbContext _dbContext { get; set; } public async Task<T> GetAsync(string id) { return await _dbContext.FindAsync<T>(id); } public IQueryable<T> Query() { return _dbContext.Set<T>().AsQueryable(); } public async Task InsertAsync(T entity) { _dbContext.Set<T>().Add(entity); await _dbContext.SaveChangesAsync(); } public async Task UpdateAsync(T entity) { _dbContext.Entry(entity).State = EntityState.Modified; await _dbContext.SaveChangesAsync(); } } public interface IPanelRepository : IGenericRepository<Panel> { } public class PanelRepository : GenericRepository<Panel>, IPanelRepository { public PanelRepository(CrossSolarDbContext dbContext) { _dbContext = dbContext; } } [Route("[controller]")] public class PanelController : Controller { private readonly IPanelRepository _panelRepository; public PanelController(IPanelRepository panelRepository) { _panelRepository = panelRepository; } // GET panel/XXXX1111YYYY2222 [HttpGet("{panelId}")] public async Task<IActionResult> Get([FromRoute] string panelId) { Panel panel = await _panelRepository.Query().FirstOrDefaultAsync(x => x.Serial.Equals(panelId, StringComparison.CurrentCultureIgnoreCase)); if (panel == null) return NotFound(); return Ok(panel); } } public class PanelControllerTests { private readonly PanelController _panelController; private static readonly Panel panel = new Panel { Id = 1, Brand = "Areva", Latitude = 12.345678, Longitude = 98.7655432, Serial = "AAAA1111BBBB2222" }; private readonly IQueryable<Panel> panels = new List<Panel>() { panel }.AsQueryable(); private readonly Mock<IPanelRepository> _panelRepositoryMock = new Mock<IPanelRepository>(); public PanelControllerTests() { _panelRepositoryMock.Setup(x => x.Query()).Returns(panels); // I also tried this. I got another error 'Invalid setup on an extension method: x => x.FirstOrDefaultAsync<Panel>(It.IsAny<Expression<Func<Panel, Boolean>>>(), CancellationToken)' // _panelRepositoryMock.As<IQueryable<Panel>>().Setup(x => x.FirstOrDefaultAsync(It.IsAny<Expression<Func<Panel, bool>>>(), default(CancellationToken))).ReturnsAsync(panel); _panelController = new PanelController(_panelRepositoryMock.Object); } [Fact] public async Task Register_ShouldInsertOneHourElectricity() { IActionResult result = await _panelController.Get("AAAA1111BBBB2222"); Assert.NotNull(result); var createdResult = result as CreatedResult; Assert.NotNull(createdResult); Assert.Equal(201, createdResult.StatusCode); } } 

I'm getting this error

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

I think that I need to mock 'FirstOrDefaultAsync' but I'm not sure and I don't know how to do. I tried something, however it couldn't be compiled.

4

6 Answers

I get stuck on this issue today and this lib resolve it for me completely, please refer:

Mocking Entity Framework Core operations such ToListAsync, FirstOrDefaultAsync etc.

//1 - create a List<T> with test items var users = new List<UserEntity>() { new UserEntity{LastName = "ExistLastName", DateOfBirth = DateTime.Parse("01/20/2012")}, ... }; //2 - build mock by extension var mock = users.AsQueryable().BuildMock(); //3 - setup the mock as Queryable for Moq _userRepository.Setup(x => x.GetQueryable()).Returns(mock.Object); //3 - setup the mock as Queryable for NSubstitute _userRepository.GetQueryable().Returns(mock); 
4

You could implement an AsyncEnumerable which can be used like this:

private readonly IQueryable<Panel> panels = new AsyncEnumerable(new List<Panel>() { panel }); 

Here is the implementation of it:

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> GetEnumerator() { return new AsyncEnumerator<T>(this.AsEnumerable().GetEnumerator()); } IQueryProvider IQueryable.Provider => new AsyncQueryProvider<T>(this); } 

The AsyncEnumerator class:

public class AsyncEnumerator<T> : IAsyncEnumerator<T> { private readonly IEnumerator<T> _inner; public AsyncEnumerator(IEnumerator<T> inner) { _inner = inner; } public void Dispose() { _inner.Dispose(); } public T Current => _inner.Current; public Task<bool> MoveNext(CancellationToken cancellationToken) { return Task.FromResult(_inner.MoveNext()); } } 

The AsyncQueryProvider class:

public class AsyncQueryProvider<TEntity> : IAsyncQueryProvider { private readonly IQueryProvider _inner; internal 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)); } } 
8

This is because of your mocking approach; your mock provider just returns panels for Query, and panels is a simple object with LINQ-to-Objects exposing it as queryable:

private readonly IQueryable<Panel> panels = new List<Panel>() { panel }.AsQueryable(); 

Indeed, this does not implement IAsyncQueryProvider. If you can get hold of the regular query provider, you should be able to wrap that with a fake always-synchronous version to spoof it (just use return Task.FromResult(Execute(expression))), but frankly I'm not sure that this would be a useful test... at that point you're skipping so many of the important realities of async that it probably isn't worth it.

7

For issue in entity framework core ,use Moq.EntityFrameworkCore library and setup your dbset as below:

contextMock.Setup(x => x.entity).ReturnsDbSet(entityList); 
1

I was facing the same issue and solved it that way:

Instead of having my method public async Task I switched it to public void and then resolved the asynchronous methods with .GetAwaiter() instead of await.

When querying for panel, removing the async in firstordefault.

Also remove the async from tolist when querying for analytics

2

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

You Might Also Like