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问题:

  1. 将我的项目目标从.NetFramework 4.0更改为.NetFramework 4.5

  2. 重新安装EntityFramework 6.1.3 Nuget包

  3. 此时,我的Visual Studio的IDE Show Potencial Fixes顾问,允许我引用System.Data.Entity.Infrastructure命名空间

使用System.Data.Entity.Infrastructure ;

DbSet可能隐式实现IDbSet因此这些方法不适用于派生类中的接口映射 。

不要从IDbSet派生。

您无法通过重新实现该接口来调用IDbSet的显式实现的接口成员。