AutoMapper не может использовать TestDbAsyncEnumerable для IQueryable
Я реализовал подделки TestDbAsync https://msdn.microsoft.com/en-us/library/dn314429(v=vs.113).aspx, и я хочу, чтобы AutoMapper мог использовать проект для другого типа перед вызовом методы Async EF (ToListAsync, CountAsync и т.д.).
Я получаю исключение исключения в ProjectionExpression. Для
Пример кода, который генерирует исключение.
_userRepository.GetAll().OrderBy(x => x.Id).ProjectTo<User>.ToListAsync();
Это отлично работает в не-тестовом сценарии, но когда я издеваюсь над DbSet, используя TestDbAsyncEnumerable, я получаю
: Unable to cast object of type 'Namespace.TestDbAsyncEnumerable`1[UserEntity]' to type 'System.Linq.IQueryable`1[User]'.
Теперь, чтобы обойти это, мне нужно ProjectTo после вызова расширений Async EF. Есть ли способ сохранить вызов ProjectTo перед расширениями EF?
Код ссылки:
public class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>
{
public TestDbAsyncEnumerable(IEnumerable<T> enumerable)
: base(enumerable)
{ }
public TestDbAsyncEnumerable(Expression expression)
: base(expression)
{ }
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
IQueryProvider IQueryable.Provider => new TestDbAsyncQueryProvider<T>(this);
}
public static Mock<DbSet<T>> ToAsyncDbSetMock<T>(this IEnumerable<T> source)
where T : class
{
var data = source.AsQueryable();
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;
}
Ответы
Ответ 1
Отредактируйте свой TestDbAsyncQueryProvider<>.CreateQuery()
так, чтобы он возвращал правильный тип выражения, переданного ProjectTo<>
.
Вот мой пример реализации.
public IQueryable CreateQuery(Expression expression)
{
switch (expression)
{
case MethodCallExpression m:
{
var resultType = m.Method.ReturnType; // it shoud be IQueryable<T>
var tElement = resultType.GetGenericArguments()[0];
var queryType = typeof(TestDbAsyncEnumerable<>).MakeGenericType(tElement);
return (IQueryable)Activator.CreateInstance(queryType, expression);
}
}
return new TestDbAsyncEnumerable<TEntity>(expression);
}
https://gist.github.com/masaedw/95ab972f8181de6bbe48a20ffe9be113
Я написал также unit тест. Это работает.
https://github.com/masaedw/AutoMapper/blob/TestDbAsync/src/IntegrationTests/MockedContextTests.cs
Ответ 2
Я получал эту же ошибку в своих тестах после обновления с Automapper 6.0.2 до 6.1.1. Проблема понизилась до версии 6.0.2.
Не уверен, что это регрессия или нарушение в Automapper. У меня не было времени, чтобы продолжить его, кроме рассмотрения журналов изменений и проблем github. Ничего не выпрыгивает.
Ответ 3
Я столкнулся с этой же проблемой, в дополнение к принятому ответу у вас также может быть общая версия CreateQuery, как и я, - я тоже исправил это:
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
var queryType = typeof(TestDbAsyncEnumerable<>).MakeGenericType(typeof(TElement));
return (IQueryable<TElement>)Activator.CreateInstance(queryType, expression);
}
Тип предоставляется TElement, поэтому его более простая реализация в общей версии.