前言
我计算了大约20个与此特定错误有关的问题,但我没有发现它们中的任何一个适用。我正在做一些不同的事情,因为我以编程方式而不是文字lambda语法创建表达式。我怀疑这可能会发现其他并发症。
下面的代码使用了我的一些助手以及我自己的UnitOfWork类。为简洁起见,我不会在此处拖动所有内容,但会根据请求提供任何其他代码。
问题
我正在尝试对IQueryable
执行SelectMany
次操作。它在我按字面意思写出表达式时起作用。但是,因为我需要能够动态地构建所有这些,所以我没有文字表达式的选项。
请参阅以下代码以获取评论。
Unable to cast the type 'IQueryable`1[[SystemAssociateModel]]' to type 'IEnumerable`1[[SystemAssociateModel]]'.
LINQ to Entities only supports casting EDM primitive or enumeration types.
☺
public static void SelectManyTest()
{
int id = 14690;
var p = Expression.Parameter(typeof(SystemEntitlement));
var projector = ExpressionHelpers.GetLambda<SystemAssociate, SystemAssociateModel>(x => new SystemAssociateModel
{
role = x.Role.Name,
id = x.Associate.Id,
order = x.Order
});
var memberPath = ExpressionHelpers.GetLambda<SystemEntitlement, ICollection<SystemAssociate>>(
x => x.System.Associates).Body.AsMemberExpression().ReplaceInstance(p);
//These two calls equate to this: x.System.Associates.AsQueryable().Select(projector)
var call_asQueryable = Expression.Call(ExpressionHelpers.GetMethodInfo(() => Queryable.AsQueryable<SystemAssociate>(null)), memberPath);
Expression call_select = Expression.Call(ExpressionHelpers.GetMethodInfo(
() => Queryable.Select(default(IQueryable<SystemAssociate>), default(Expression<Func<SystemAssociate, SystemAssociateModel>>))),
call_asQueryable, projector);
//I use this in an attempt to cast my `Select` into `IEnumerable` for `SelectMany`. This
//is most likely where the issue is occurring. It makes sense that only specific types of
//casts would be supported within an expression.
//call_select = Expression.Convert(call_select, typeof(IEnumerable<SystemAssociateModel>));
//I have to use the uncommented line since that's the only one suitable for `.SelectMany`.
//This is the reason for the cast above, to convert `Select`'s `IQueryable` into an
//`IEnumerable` for `SelectMany`. If I don't use the explicit cast, it will attempt an
//implicit cast and still fail.
//var selector = (Expression<Func<SystemEntitlement, IQueryable<SystemAssociateModel>>>)Expression.Lambda(call_select, masterp);
var selector = (Expression<Func<SystemEntitlement, IEnumerable<SystemAssociateModel>>>)Expression.Lambda(call_select, p);
//This works so long as I write the `.SelectMany` expression literally. I seems that the compiler is somehow
//able to handle the implicit cast from `ICollection` to `IEnumerable`.
var associates1 = UnitOfWork.UseWith(work => work.GetRepo<SystemEntitlement>().AsQueryable()
.Where(x => x.Id == id)
.SelectMany(x => x.System.Associates.AsQueryable().Select(projector))
.Where(x => x.order == 1)
.ToArray());
//This throws the error in question.
var associates2 = UnitOfWork.UseWith(work => work.GetRepo<SystemEntitlement>().AsQueryable()
.Where(pred)
.SelectMany(selector)
.Where(x => x.order == 1)
.ToArray());
}
答案 0 :(得分:2)
EF不支持此类强制转换,因此不应使用Expression.Convert
。
实际问题是Expression.Lambda(call_select, p)
会将Expression<Func<T, IQueryable<R>>>
返回到Expression<Func<T, IEnumerable<R>>>
。但Expression
因为任何C#类都不支持方差,因此预期会失败。
解决方案是使用Expression.Lambda
方法重载,允许您指定所需的结果类型。在您的示例中,它可能是这样的:
var selector = Expression.Lambda<Func<SystemEntitlement, IEnumerable<SystemAssociateModel>>>(
call_select, p);
一旦你这样做,问题就会解决。
作为旁注 - 当您动态构建表达式时,不需要在集合导航属性上应用AsQueryable
- 它用于欺骗C#编译器以允许使用Expression<Func<...>>
变量在你期望Func<...>
的方法中调用文字表达式的内部。当您手动发出Expression.Call
时,您只需调用相应的Enumerable
方法:
var call_select = Expression.Call(ExpressionHelpers.GetMethodInfo(
() => Enumerable.Select(default(IEnumerable<SystemAssociate>), default(Func<SystemAssociate, SystemAssociateModel>))),
memberPath, projector);
我在这里猜测你ExpressionHelpers.GetMethodInfo
的预期参数。我个人&#34;打电话给#34;像这样的通用方法:
var call_select = Expression.Call(
typeof(Enumerable), "Select", new[] { typeof(SystemAssociate), typeof(SystemAssociateModel) },
memberPath, projector);
当然AsQueryable
在EF6中并不会造成伤害,但是它是多余的,并且在EF Core中不受支持。