4

I'm using the below link for mocking the FromSql method in x unit How could I Mock the FromSql() method?

I'm getting the below error for the method which uses FirstOrDefaultAsync

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

But it is working fine for the ToListAsync method.

SpAsyncEnumerableQueryable<Model> models = new SpAsyncEnumerableQueryable<Model>();
models.Add(new Model { ItemId = 1 });

MyDbContext.Model = MyDbContext.Model.MockFromSql(models);

Below is the Actual method c#

return await this.MyDbContext.Model
    .FromSql("TestProc", 1, 1)
    .FirstOrDefaultAsync()
    .ConfigureAwait(false);

Getting error at FirstOrDefaultAsync

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

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
user3240168
  • 41
  • 1
  • 1
  • 4

1 Answers1

0

The exception message is telling you the problem, the FromSql returned sequence provider isn't implementing IAsyncQueryProvider. Your FromSql mock set up needs to return a sequence that has an IAsyncQueryProvider provider.

I think there are a couple of ways you could it. One is create your own AsyncQueryProvider that implements IAsyncQueryProvider and then ensure your FromSql mock invocations return a sequence with it set as the provider. This will lead you to implementing classes that implement IAsyncEnumerator and IAsyncEnumerable as well.

Another option that may work is when setting up your FromSql mock, transform your return sequence to a Queryable then to an AsyncEnumerable. Off the top of my head that'd look something like enumerable.AsQueryable().ToAsyncEnumerable(). This is what I currently do when setting up DbQuery mocks and it seems to hold up for the async LINQ operations.

All that being said, having done it for EntityFrameworkCore.Testing which does support FirstOrDefaultAsync on the back of a FromSql invocation, save yourself some time and use one of the libraries that do this mucking around for you.

[Test]
public virtual async Task FromSqlThenFirstOrDefaultAsync_ReturnsFirstElement()
{
    var storedProcedureReturns = Fixture.CreateMany<TestEntity>().ToList();
    var mockedDbContext = 
        Create.MockedDbContextFor(new TestDbContext(new DbContextOptionsBuilder<TestDbContext>().UseInMemoryDatabase(Guid.NewGuid().ToString()).Options));
    mockedDbContext.Set<TestEntity>().AddFromSqlResult("usp_SomeStoredProcedure", storedProcedureReturns);

    var actualResult = await mockedDbContext.Set<TestEntity>().FromSql("[dbo].[usp_SomeStoredProcedure] @Parameter1, @Parameter2").FirstOrDefaultAsync();

    Assert.That(actualResult, Is.EqualTo(storedProcedureReturns.First()));
}
rgvlee
  • 2,773
  • 1
  • 13
  • 20