IDbAsyncEnumerable not implemented
C#Entity FrameworkC# Problem Overview
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
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
C# Solutions
Solution 1 - C#
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 2 - C#
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
> 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 3 - C#
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 - C#
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 - C#
To solved my IDbAsyncEnumerable issue:
-
Changed my project's target from .NetFramework 4.0 to .NetFramework 4.5
-
Reinstalled EntityFramework 6.1.3 Nuget package.
-
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 - C#
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>
.
Solution 7 - C#
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).