Ghosttje Ghosttje - 1 month ago 12
C# Question

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<Conexio.Data.Entities.Conexio.ConexioContact>. 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

Answer

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...