C# AutoMapper无法将TestDbAsyncEnumerable强制转换为IQueryable
我已经从中实现了TestDbAsync fakes,我希望能够在调用Async EF方法(ToListAsync、CountAsync等)之前使用AutoMapper投影到不同的类型 我在ProjectionExpression.To中遇到强制转换异常 引发异常的示例代码C# AutoMapper无法将TestDbAsyncEnumerable强制转换为IQueryable,c#,entity-framework,automapper,C#,Entity Framework,Automapper,我已经从中实现了TestDbAsync fakes,我希望能够在调用Async EF方法(ToListAsync、CountAsync等)之前使用AutoMapper投影到不同的类型 我在ProjectionExpression.To中遇到强制转换异常 引发异常的示例代码 _userRepository.GetAll().OrderBy(x => x.Id).ProjectTo<User>.ToListAsync(); 现在为了解决这个问题,我必须在调用Async EF扩展之
_userRepository.GetAll().OrderBy(x => x.Id).ProjectTo<User>.ToListAsync();
现在为了解决这个问题,我必须在调用Async EF扩展之后进行ProjectTo。有没有办法让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;
}
公共类TestDbAsyncEnumerable:EnumerableQuery、IDbAsyncEnumerable、IQueryable
{
公共TestDbAsyncEnumerable(IEnumerable enumerable)
:基本(可枚举)
{ }
公共TestDbAsyncEnumerable(表达式)
:base(表达式)
{ }
公共IDbAsyncEnumerator GetAsyncEnumerator()
{
返回新的TestDbAsyncEnumerator(this.AsEnumerable().GetEnumerator());
}
IDBSyncEnumerator IDBSyncEnumeratable.GetAsyncEnumerator()
{
返回GetAsyncEnumerator();
}
IQueryProvider IQueryable.Provider=>新的TestDbAsyncQueryProvider(此);
}
公共静态模拟到SyncDBSetMock(此IEnumerable源)
T:在哪里上课
{
var data=source.AsQueryable();
var mockSet=new Mock();
mockSet.As()
.Setup(m=>m.GetAsyncEnumerator())
.Returns(新的TestDbAsyncEnumerator(data.GetEnumerator());
mockSet.As()
.Setup(m=>m.Provider)
.Returns(新的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());
返回模拟集;
}
从Automapper 6.0.2升级到6.1.1后,我在测试中遇到了同样的错误。降级回6.0.2修复了该问题
不确定这是Automapper的回归还是突破性变化。除了查看更改日志和github问题,我没有时间进一步研究它。没有跳出任何内容。编辑您的
TestDbAsyncQueryProvider.CreateQuery()
,以便它返回由项目传递到的正确类型的表达式
这是我的示例实现
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);
}
public IQueryable CreateQuery(表达式)
{
开关(表达式)
{
case MethodCallExpression m:
{
var resultType=m.Method.ReturnType;//它应该是可查询的
var tElement=resultType.GetGenericArguments()[0];
var queryType=typeof(TestDbAsyncEnumerable);
return(IQueryable)Activator.CreateInstance(queryType,expression);
}
}
返回新的TestDbAsyncEnumerable(表达式);
}
我还编写了单元测试。它起作用了
我遇到了同样的问题,除了公认的答案之外,您可能也会像我一样拥有CreateQuery的通用版本-我也这样修复了这个问题:
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
var queryType = typeof(TestDbAsyncEnumerable<>).MakeGenericType(typeof(TElement));
return (IQueryable<TElement>)Activator.CreateInstance(queryType, expression);
}
public IQueryable CreateQuery(表达式)
{
var queryType=typeof(TestDbAsyncEnumerable)。MakeGenericType(typeof(TElement));
return(IQueryable)Activator.CreateInstance(queryType,expression);
}
该类型由TElement提供,因此它在通用版本上的实现更简单。通过结合使用上述两个答案,帮助我在单元测试中使用.ProjectTo
在TestDbAsyncQueryProvider实现中,我替换了以下两种方法:
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);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
var queryType = typeof(TestDbAsyncEnumerable<>).MakeGenericType(typeof(TElement));
return (IQueryable<TElement>)Activator.CreateInstance(queryType, expression);
}
public IQueryable CreateQuery(表达式)
{
开关(表达式)
{
case MethodCallExpression m:
{
var resultType=m.Method.ReturnType;//它应该是可查询的
var tElement=resultType.GetGenericArguments()[0];
var queryType=typeof(TestDbAsyncEnumerable);
return(IQueryable)Activator.CreateInstance(queryType,expression);
}
}
返回新的TestDbAsyncEnumerable(表达式);
}
公共IQueryable CreateQuery(表达式)
{
var queryType=typeof(TestDbAsyncEnumerable)。MakeGenericType(typeof(TElement));
return(IQueryable)Activator.CreateInstance(queryType,expression);
}
它可以与今天最新的EF6配合使用。如果实体上有人有其他对象,ProjectTo将崩溃或抛出带有空引用对象的异常
解决方案是确保您的实体的所有对象都用default()或其他东西初始化
在此处的最后一条评论/答案中找到答案->
另外,感谢IAsyncQueryProvider的实现:)没有考虑降级。我们最终将EF调用抽象到一个接口后面进行测试。如果有人对贡献感兴趣,我们也可以在AM内部解决。很高兴看到有人跟踪根本原因。我喜欢你的产品!谢谢拯救了我的一天:P
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);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
var queryType = typeof(TestDbAsyncEnumerable<>).MakeGenericType(typeof(TElement));
return (IQueryable<TElement>)Activator.CreateInstance(queryType, expression);
}