IDbAsyncQueryProvider in EntityFrameworkCore

I use XUNIT for testing in a kernel with a network core.

I need to test a service that internally creates an asynchronous DbSet request in my datacontext.

I saw here that it is mocking that DbSet is asynchronously possible.

The problem I am facing is that the IDbAsyncQueryProvider does not seem to be available in the EntityframeworkCore that I use.

Am I incorrect here? Has anyone else got this job?

(It was a long day, I hope I just missed something simple)

EDIT

After a GitHub request, I got an indication of this class: https://github.com/aspnet/EntityFramework/blob/dev/src/Microsoft.EntityFrameworkCore/Query/Internal/IAsyncQueryProvider.cs

This is what I still tried to implement:

 using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.Query.Internal; namespace EFCoreTestQueryProvider { internal class TestAsyncQueryProvider<TEntity>: IAsyncQueryProvider { private readonly IQueryProvider _inner; internal TestAsyncQueryProvider(IQueryProvider inner) { _inner = inner; } IQueryable CreateQuery(Expression expression) { return new TestDbAsyncEnumerable<TEntity>(expression); } IQueryable<TElement> CreateQuery<TElement>(Expression expression) { return new TestDbAsyncEnumerable<TElement>(expression); } object Execute(Expression expression) { return _inner.Execute(expression); } TResult Execute<TResult>(Expression expression) { return _inner.Execute<TResult>(expression); } IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression expression) { return Task.FromResult(Execute<TResult>(expression)).ToAsyncEnumerable(); } Task<TResult> IAsyncQueryProvider.ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken) { return Task.FromResult(Execute<TResult>(expression)); } } internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, System.Collections.Generic.IAsyncEnumerable<T>, IQueryable<T> { public TestDbAsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable) { } public TestDbAsyncEnumerable(Expression expression) : base(expression) { } public IAsyncEnumerator<T> GetAsyncEnumerator() { return new TestDbAsyncEnumerable<T>(this.AsEnumerable()).ToAsyncEnumerable(); } IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() { return GetAsyncEnumerator(); } IAsyncEnumerator<T> IAsyncEnumerable<T>.GetEnumerator() { throw new NotImplementedException(); } IQueryProvider IQueryable.Provider { get { return new TestAsyncQueryProvider<T>(this); } } } } 

Now I tried to implement this and ran into some additional problems, especially around these two methods:

 public IAsyncEnumerator<T> GetAsyncEnumerator() { return new TestDbAsyncEnumerable<T>(this.AsEnumerable()).ToAsyncEnumerable(); } IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() { return GetAsyncEnumerator(); } 

I hope someone can point me in the right direction regarding what I am doing wrong.

+5
source share
1 answer

I finally got it to work. They slightly changed the interfaces in the EntityFrameworkCore from IDbAsyncEnumerable to IAsyncEnumerable , so the following code worked for me:

 public class AsyncEnumerable<T> : EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T> { public AsyncEnumerable(Expression expression) : base(expression) { } public IAsyncEnumerator<T> GetEnumerator() => new AsyncEnumerator<T>(this.AsEnumerable().GetEnumerator()); } public class AsyncEnumerator<T> : IAsyncEnumerator<T> { private readonly IEnumerator<T> enumerator; public AsyncEnumerator(IEnumerator<T> enumerator) => this.enumerator = enumerator ?? throw new ArgumentNullException(); public T Current => enumerator.Current; public void Dispose() { } public Task<bool> MoveNext(CancellationToken cancellationToken) => Task.FromResult(enumerator.MoveNext()); } [Fact] public async Task TestEFCore() { var data = new List<Entity>() { new Entity(), new Entity(), new Entity() }.AsQueryable(); var mockDbSet = new Mock<DbSet<Entity>>(); mockDbSet.As<IAsyncEnumerable<Entity>>() .Setup(d => d.GetEnumerator()) .Returns(new AsyncEnumerator<Entity>(data.GetEnumerator())); mockDbSet.As<IQueryable<Entity>>().Setup(m => m.Provider).Returns(data.Provider); mockDbSet.As<IQueryable<Entity>>().Setup(m => m.Expression).Returns(data.Expression); mockDbSet.As<IQueryable<Entity>>().Setup(m => m.ElementType).Returns(data.ElementType); mockDbSet.As<IQueryable<Entity>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator()); var mockCtx = new Mock<SomeDbContext>(); mockCtx.SetupGet(c => c.Entities).Returns(mockDbSet.Object); var entities = await mockCtx.Object.Entities.ToListAsync(); Assert.NotNull(entities); Assert.Equal(3, entities.Count()); } 

You may be able to further refine those test implementations of AsyncEnumerable and AsyncEnumerator . I did not try, I just got it for work.

Remember that your DbSet on your DbContext must be marked virtual , otherwise you will need to implement some interface wrapper over DbContext in order to make this work properly.

+5
source

All Articles