IDbAsyncEnumerable未实现

kev*_*oos 22 c# entity-framework

我正在尝试用FakeDbSet制作一个FakeDbContext进行单元测试.

但是我收到以下错误(见下文).我正在扩展DbSet,所以通常应该实现IDbAsyncEnumerable.当我实现它时,它说没有用.

例外:

System.InvalidOperationException:源IQueryable未实现IDbAsyncEnumerable.只有实现IDbAsyncEnumerable的源才能用于Entity Framework异步操作.有关详细信息,请参阅 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; }
    }
Run Code Online (Sandbox Code Playgroud)

这是代码的要点.在gist的最后一个文件中,这是错误发生的地方. 要点代码

Han*_*ant 30

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

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

我可以补充一点,我只会引用基本短语:

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

System.InvalidOperationException:源IQueryable未实现IDbAsyncEnumerable.只有实现IDbAsyncEnumerable的源才能用于Entity Framework异步操作.有关详细信息,请参阅http://go.microsoft.com/fwlink/?LinkId=287068.

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

等等...


t3c*_*b0t 24

在我的情况下,异常是由使用错误的ToListAsync扩展名引起的.

它来自:

using System.Data.Entity;
Run Code Online (Sandbox Code Playgroud)

代替

using Microsoft.EntityFrameworkCore;
Run Code Online (Sandbox Code Playgroud)

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

  • 这应该是一个可以接受的答案 - 我花了很长时间才找到这个答案。谢谢 (3认同)
  • 这也是我的问题!作为记录,我发现将漂亮的抽象拆分为具体的 EFCore 实现真的很讨厌,并迫使 DEV 绑定到 EFCore………… (2认同)
  • 每次都让我着迷。 (2认同)

Ton*_*gan 10

我从这里重命名了示例测试类以删除该单词,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
Run Code Online (Sandbox Code Playgroud)

这不仅在单元测试中有用,而且当您想要实现一个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);
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

对于使用Microsoft上面讨论的样板代码的人来说,这是一个快速帮助类,它可以将您的模拟数据转换为异步结果.只需添加到MS代码的底部并使用类似的方式调用

        var fakeDateAsMockAsyncQueryResult = new MockAsyncData<SomeType>().MockAsyncQueryResult(fakeDataList.AsQueryable());
Run Code Online (Sandbox Code Playgroud)

.......

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

它与MS示例中的代码相同,但是通用且可从许多不同的单元测试中重用.