以下是实现
IQueryable<T>.Except
的代码,可以在
这里 进行查看:
public static IQueryable<TSource> Except<TSource>(this IQueryable<TSource> source1, IEnumerable<TSource> source2) {
if (source1 == null)
throw Error.ArgumentNull("source1");
if (source2 == null)
throw Error.ArgumentNull("source2");
return source1.Provider.CreateQuery<TSource>(
Expression.Call(
null,
GetMethodInfo(Queryable.Except, source1, source2),
new Expression[] { source1.Expression, GetSourceExpression(source2) }
));
}
工作方式上 IQueryable 和 List 的主要区别在于,Queryable 类型在内部使用 Expression> 进行操作,因为它是远程执行的,例如在您的情况下使用提供程序执行时,而 List 则使用 Func 进行内存处理。当涉及到类似 EF 这样的远程处理时,它会将相关的 SQL 查询转换为处理语句,而在您的情况下,bs.Select(b => b.ToType()) 在远程处理过程中会转换为 null。
以下是 IEnumerable.Except 的实现,可在此处查看:
here
public static IEnumerable<TSource> Except<TSource>(this IEnumerable<TSource> first,
IEnumerable<TSource> second)
{
if (first == null) throw Error.ArgumentNull("first");
if (second == null) throw Error.ArgumentNull("second");
return ExceptIterator<TSource>(first, second, null);
}
“Except”本身是一个集合操作,即使对于“List<T>”,调用“Except(null)”也会导致相同的异常。
正如您所看到的,“IQueryable<T>.Except”的定义,重要的是要理解“Expression和Func”的处理方式的差异,表达式更多关注做什么,而Func则是关于如何检查
this。
对于一个简单的“var intList = new List<int>{1,2,3}”,这就是可查询表达式的样子(如附图所示)。
实质上,需要检查您的提供程序内部将Queryable Expression转换为什么,这导致了空值,因此在处理过程中出现异常。
![enter image description here](https://istack.dev59.com/LYjDs.webp)
IQueryable<P> ps2 = bs.Select(b => b.ToType());
,会发生什么?如果在ps2
上调用.ToList
会发生什么?当然有限制——不是在表达式树中(虽然也有这样的限制)——而是更多地涉及表达式树的哪些部分可以转换为SQL(或者底层数据访问语言)。例如,针对SQL Server的EF6将无法处理对.ToString
的调用,因为没有简单的方法将其转换为SQL。但如果是这种情况... - Zev Spitzbs.Select
实际上由于某种原因调用了Enumerable.Select
,并且bs
中的一个结果是null
的原因。 - Zev SpitzList<P> p1 = ps2.ToList()
可以正常工作,如果在此之后执行ps.Except(p1)
也可以正常工作。 - Kristen Hammack