Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 692
sinanakyazici Avatar asked Jun 25 '18 12:06

sinanakyazici


Video Answer


2 Answers

I get stuck on this issue today and this lib resolve it for me https://github.com/romantitov/MockQueryable 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); 
like image 70
Do Tat Hoan Avatar answered Sep 17 '22 22:09

Do Tat Hoan


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));     } } 
like image 27
Sebastian Krogull Avatar answered Sep 20 '22 22:09

Sebastian Krogull