IDbAsyncEnumerable未实现
我正在尝试用FakeDbSet制作一个FakeDbContext进行unit testing。
但是我得到以下错误(见下文)。 我正在扩展DbSet,所以通常应该实现IDbAsyncEnumerable。 当我实现它时,它说没有用。
例外:
System.InvalidOperationException:源IQueryable未实现IDbAsyncEnumerable。 只有实现IDbAsyncEnumerable的源才能用于Entity Framework异步操作。 有关详细信息,请参阅http://go.microsoft.com/fwlink/?LinkId=287068 。
FakeDbSet类:
public abstract class FakeDbSet : DbSet, IEnumerable, IQueryable, IDbAsyncEnumerable where TEntity : Entity, new() { #region Private Fields private readonly ObservableCollection _items; private readonly IQueryable _query; #endregion Private Fields protected FakeDbSet() { _items = new ObservableCollection(); _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() { return Activator.CreateInstance(); } public override ObservableCollection Local { get { return _items; } } IEnumerator IEnumerable.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; } }
这是代码的要点。 在gist的最后一个文件中,这是错误发生的地方。 要点代码
您的方案在随exception消息提供的链接中明确提到( http://go.microsoft.com/fwlink/?LinkId=287068 )。 缺少的成分是您应该从Provider属性返回的IDbAsyncQueryProvider。
只需浏览链接即可获得样板实现 。
我可以补充一点,我只会引用基本短语:
为了使用异步查询,我们需要做更多的工作。 如果我们尝试将我们的Moq DbSet与GetAllBlogsAsync方法一起使用,我们将得到以下exception:
System.InvalidOperationException:源IQueryable未实现IDbAsyncEnumerable。 只有实现IDbAsyncEnumerable的源才能用于Entity Framework异步操作。 有关详细信息,请参阅http://go.microsoft.com/fwlink/?LinkId=287068 。
为了使用异步方法,我们需要创建一个内存中的DbAsyncQueryProvider来处理异步查询。 虽然可以使用Moq设置查询提供程序,但在代码中创建测试双重实现要容易得多。 此实现的代码如下:
等等…
我从这里重命名了示例测试类以删除单词Test
因为它们在测试之外是有用的:
-
DbAsyncEnumerable
-
DbAsyncEnumerator
-
DbAsyncQueryProvider
然后我在下面添加了扩展类,所以你现在可以做…
var data = new List { new Blog { Name = "BBB" }, new Blog { Name = "ZZZ" }, new Blog { Name = "AAA" }, }.AsAsyncQueryable(); // <<== new extension method
这不仅在unit testing中有用,而且当您想要实现IQueryable
接口时,该接口要么返回异步数据库查询,要么在内存数据中,您可以随后安全地调用它作为query.ToAsyncArray()
。
public static class AsyncQueryableExtensions { public static IQueryable AsAsyncQueryable (this IEnumerable source) { return new DbAsyncEnumerable (source); } public static IDbAsyncEnumerable AsDbAsyncEnumerable (this IEnumerable source) { return new DbAsyncEnumerable (source); } public static EnumerableQuery AsAsyncEnumerableQuery (this IEnumerable source) { return new DbAsyncEnumerable (source); } public static IQueryable AsAsyncQueryable (this Expression expression) { return new DbAsyncEnumerable (expression); } public static IDbAsyncEnumerable AsDbAsyncEnumerable (this Expression expression) { return new DbAsyncEnumerable (expression); } public static EnumerableQuery AsAsyncEnumerableQuery (this Expression expression) { return new DbAsyncEnumerable (expression); } }
对于使用Microsoft上面讨论的样板代码的人来说,这是一个快速帮助类,它可以将您的模拟数据转换为异步结果。 只需添加到MS代码的底部并使用类似的方式调用
var fakeDateAsMockAsyncQueryResult = new MockAsyncData().MockAsyncQueryResult(fakeDataList.AsQueryable());
…….
internal class MockAsyncData where T : class { public Mock> MockAsyncQueryResult(IQueryable data) { var mockSet = new Mock>(); mockSet.As>() .Setup(m => m.GetAsyncEnumerator()) .Returns(new TestDbAsyncEnumerator (data.GetEnumerator())); mockSet.As>() .Setup(m => m.Provider) .Returns(new TestDbAsyncQueryProvider (data.Provider)); mockSet.As>().Setup(m => m.Expression).Returns(data.Expression); mockSet.As>().Setup(m => m.ElementType).Returns(data.ElementType); mockSet.As>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator()); return mockSet; } }
它与MS示例中的代码相同,但是通用且可从许多不同的unit testing中重复使用。
在我的情况下,exception是由使用错误的ToListAsync
扩展引起的。
它来自:
using System.Data.Entity;
代替
using Microsoft.EntityFrameworkCore;
更改命名空间修复了错误。
解决了我的IDbAsyncEnumerable问题:
-
将我的项目目标从.NetFramework 4.0更改为.NetFramework 4.5
-
重新安装EntityFramework 6.1.3 Nuget包 。
-
此时,我的Visual Studio的IDE Show Potencial Fixes顾问,允许我引用System.Data.Entity.Infrastructure命名空间
使用System.Data.Entity.Infrastructure ;
DbSet
可能隐式实现IDbSet
因此这些方法不适用于派生类中的接口映射 。
不要从IDbSet
派生。
您无法通过重新实现该接口来调用IDbSet
的显式实现的接口成员。