'IDbAsyncEnumerable not implemented

I am trying to make a FakeDbContext with a FakeDbSet for unit testing.

But I get the following error (see below). I am extending DbSet so normally IDbAsyncEnumerable should be implemented. And when I implement it, it says that it has no use.

Exception:

System.InvalidOperationException: The source IQueryable doesn't implement IDbAsyncEnumerable. Only sources that implement IDbAsyncEnumerable can be used for Entity Framework asynchronous operations. For more details see http://go.microsoft.com/fwlink/?LinkId=287068.

FakeDbSet class:

public abstract class FakeDbSet<TEntity> : DbSet<TEntity>, IEnumerable<TEntity>, IQueryable, IDbAsyncEnumerable<TEntity> where TEntity : Entity, new()
{
    #region Private Fields
    private readonly ObservableCollection<TEntity> _items;
    private readonly IQueryable _query;
    #endregion Private Fields

    protected FakeDbSet()
    {
        _items = new ObservableCollection<TEntity>();
        _query = _items.AsQueryable();
    }

    public Expression Expression { get { return _query.Expression; } }

    public Type ElementType { get { return _query.ElementType; } }

    public IQueryProvider Provider { get { return _query.Provider; } }

    public override TEntity Add(TEntity entity)
    {
        _items.Add(entity);
        return entity;
    }

    public override TEntity Remove(TEntity entity)
    {
        _items.Remove(entity);
        return entity;
    }

    public override TEntity Attach(TEntity entity)
    {
        switch (entity.ObjectState)
        {
            case ObjectState.Modified:
                _items.Remove(entity);
                _items.Add(entity);
                break;

            case ObjectState.Deleted:
                _items.Remove(entity);
                break;

            case ObjectState.Unchanged:
            case ObjectState.Added:
                _items.Add(entity);
                break;

            default:
                throw new ArgumentOutOfRangeException();
        }
        return entity;
    }

    public override TEntity Create() { return new TEntity(); }

    public override TDerivedEntity Create<TDerivedEntity>() { return Activator.CreateInstance<TDerivedEntity>(); }

    public override ObservableCollection<TEntity> Local { get { return _items; } }

    IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator()
    {
        return _items.GetEnumerator();
    }

    Type IQueryable.ElementType
    {
        get { return _items.AsQueryable().ElementType; }
    }

    Expression IQueryable.Expression
    {
        get { return _items.AsQueryable().Expression; }
    }

    IQueryProvider IQueryable.Provider
    {
        get { return _items.AsQueryable().Provider; }
    }

Here is a gist with the code. In the last file in the gist, that is where the error happens. Gist code



Solution 1:[1]

Your scenario is explicitly mentioned in the link provided with the exception message (http://go.microsoft.com/fwlink/?LinkId=287068). The missing ingredient is the IDbAsyncQueryProvider that you should return from your Provider property.

Just navigate through the link to arrive at the boilerplate implementation.

Little I can add, I'll just quote the essential phrase:

In order to use asynchronous queries we need to do a little more work. If we tried to use our Moq DbSet with the GetAllBlogsAsync method we would get the following exception:

System.InvalidOperationException: The source IQueryable doesn't implement IDbAsyncEnumerable. Only sources that implement IDbAsyncEnumerable can be used for Entity Framework asynchronous operations. For more details see http://go.microsoft.com/fwlink/?LinkId=287068.

In order to use the async methods we need to create an in-memory DbAsyncQueryProvider to process the async query. Whilst it would be possible to setup a query provider using Moq, it is much easier to create a test double implementation in code. The code for this implementation is as follows:

etc...

Solution 2:[2]

In my case the exception was caused by using the wrong ToListAsync extension.

It came from:

using System.Data.Entity;

instead of

using Microsoft.EntityFrameworkCore;

Changing the namespace fixed the error.

Solution 3:[3]

I renamed the example test classes from here to remove the word Test since they are useful outside of testing:

  • DbAsyncEnumerable
  • DbAsyncEnumerator<T>
  • DbAsyncQueryProvider<TEntity>

Then I added the extension class below so you can now just do ...

var data = new List<Blog> 
{ 
    new Blog { Name = "BBB" }, 
    new Blog { Name = "ZZZ" }, 
    new Blog { Name = "AAA" }, 
}.AsAsyncQueryable();   // <<== new extension method

This is not only useful in unit tests but also when you want to implement an IQueryable<T> interface that either returns an async database query or in memory data that you can subsequently safely call as query.ToAsyncArray().

public static class AsyncQueryableExtensions
{
    public static IQueryable<TElement> AsAsyncQueryable<TElement>(this IEnumerable<TElement> source)
    {
        return new DbAsyncEnumerable<TElement>(source);
    }

    public static IDbAsyncEnumerable<TElement> AsDbAsyncEnumerable<TElement>(this IEnumerable<TElement> source)
    {
        return new DbAsyncEnumerable<TElement>(source);
    }

    public static EnumerableQuery<TElement> AsAsyncEnumerableQuery<TElement>(this IEnumerable<TElement> source)
    {
        return new DbAsyncEnumerable<TElement>(source);
    }

    public static IQueryable<TElement> AsAsyncQueryable<TElement>(this Expression expression)
    {
        return new DbAsyncEnumerable<TElement>(expression);
    }

    public static IDbAsyncEnumerable<TElement> AsDbAsyncEnumerable<TElement>(this Expression expression)
    {
        return new DbAsyncEnumerable<TElement>(expression);
    }

    public static EnumerableQuery<TElement> AsAsyncEnumerableQuery<TElement>(this Expression expression)
    {
        return new DbAsyncEnumerable<TElement>(expression);
    }
}

Solution 4:[4]

Just a note for people using the boilerplate code discussed above from Microsoft, here is a quick helper class that can transform your mocked up data into the async result. Just add to the bottom of the MS code and call with something like

var fakeDateAsMockAsyncQueryResult = new MockAsyncData<SomeType>().MockAsyncQueryResult(fakeDataList.AsQueryable());

.......

internal class MockAsyncData<T> where T : class
{ 
    public Mock<DbSet<T>> MockAsyncQueryResult(IQueryable<T> data)
    {
        var mockSet = new Mock<DbSet<T>>();

        mockSet.As<IDbAsyncEnumerable<T>>()
            .Setup(m => m.GetAsyncEnumerator())
            .Returns(new TestDbAsyncEnumerator<T>(data.GetEnumerator()));

        mockSet.As<IQueryable<T>>()
            .Setup(m => m.Provider)
            .Returns(new TestDbAsyncQueryProvider<T>(data.Provider));

        mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

        return mockSet;
    }
}

It's the same code as in the MS example but generic and reusable from many different unit tests.

Solution 5:[5]

To solved my IDbAsyncEnumerable issue:

  1. Changed my project's target from .NetFramework 4.0 to .NetFramework 4.5

  2. Reinstalled EntityFramework 6.1.3 Nuget package.

  3. At this point, my Visual Studio's IDE Show Potencial Fixes advisor, allowed me to reference System.Data.Entity.Infrastructure namespace

using System.Data.Entity.Infrastructure;

Solution 6:[6]

DbSet probably implements IDbSet implicitly so that those methods are not available for interface mapping in your derived class.

Don't derive from IDbSet<TEntity>.

You cannot call the explicitly implemented interface members of IDbSet from a re-implementation of that interface.

Solution 7:[7]

Not directly related but might help some others. If you're using DelegateCompiler, make sure you're using DecompileAsync rather than Decompile in the EF packages (source).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Hans Passant
Solution 2 t3chb0t
Solution 3 Tony O'Hagan
Solution 4 Backs
Solution 5 Julio Nobre
Solution 6 Community
Solution 7 benmccallum