IDbAsyncEnumerable not implemented

本文关键字:implemented not IDbAsyncEnumerable | 更新日期: 2023-09-27 18:26:05

我正试图用FakeDbSet制作一个FakeDbContext用于单元测试。

但我得到了以下错误(见下文)。我正在扩展DbSet,所以通常应该实现IDbSyncEnumerable。当我实施它时,它说它没有用。

例外:

System.InvalidOperationException:源IQueryable不使生效IDbSyncEnumerable。只有实现IDbSyncEnumerable的源可以用于实体框架异步操作。有关更多详细信息,请参阅http://go.microsoft.com/fwlink/?LinkId=287068.

FakeDbSet类:

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; }
    }

以下是代码的要点。在要点的最后一个文件中,这就是错误发生的地方。Gist代码

IDbAsyncEnumerable not implemented

在我的案例中,异常是由使用错误的ToListAsync扩展引起的。

它来自:

using System.Data.Entity;

而不是

using Microsoft.EntityFrameworkCore;

更改命名空间修复了错误。

异常消息提供的链接中明确提到了您的场景(http://go.microsoft.com/fwlink/?LinkId=287068)。缺少的成分是应该从Provider属性返回的IDbSyncQueryProvider。

只需浏览链接即可获得样板实现。

我只能补充一点,我只引用一句重要的话:

为了使用异步查询,我们需要做更多的工作。如果我们尝试将MoqDbSet与GetAllBlogsSync方法一起使用,我们会得到以下异常:

System.InvalidOperationException:源IQueryable未实现IDbSyncEnumerable。只有实现IDbSyncEnumerable的源才能用于实体框架异步操作。有关更多详细信息,请参阅http://go.microsoft.com/fwlink/?LinkId=287068.

为了使用异步方法,我们需要在内存中创建一个DbAsyncQueryProvider来处理异步查询。虽然可以使用Moq设置查询提供程序,但在代码中创建测试双重实现要容易得多。此实现的代码如下:

等等。。。

我从这里重命名了示例测试类,以删除单词Test,因为它们在测试之外很有用:

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

然后我在下面添加了扩展类,所以你现在可以做。。。

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

这不仅在单元测试中很有用,而且在您想要实现IQueryable<T>接口时也很有用,该接口返回异步数据库查询或内存数据,您可以随后安全地将其调用为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);
    }
}

请使用System.Data.Entity更改;使用Microsoft.EntityFrameworkCore

请注意,这里有一个快速助手类,可以将模拟数据转换为异步结果。只需添加到MS代码的底部,并使用类似的东西进行调用

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;
    }
}

它与MS示例中的代码相同,但具有通用性,可从许多不同的单元测试中重复使用。

要解决我的IDbSyncEnumerable问题:

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

  2. 已重新安装EntityFramework 6.1.3Nuget包

  3. 此时,我的Visual Studio的IDE显示潜在修复顾问允许我引用System.Data.Entity.Infrastructure命名空间

使用System.Data.Entity.Infrastructure

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

不要从IDbSet<TEntity>派生。

不能从IDbSet接口的重新实现中调用该接口的显式实现的成员。

没有直接关系,但可能有助于其他人。如果使用DelegateCompiler,请确保在EF包(源代码)中使用的是DecompileAsync而不是Decompile