为什么一个查询有效而另一个无效?

时间:2015-04-15 16:01:36

标签: c# linq entity-framework

我正在使用辅助方法根据用户访问权限预过滤我的所有查询。

假设方法签名为:

public IQueryable<Client> GetAllClients()

使用LINQ时为什么会这样做:

IQueryable<Client> allItems = GetAllClients();
return (from item in allItems
where item.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)
select item).FirstOrDefault();

但不是这样:

return (from item in GetAllClients() 
    where item.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)
    select item).FirstOrDefault();

我可以做第一个,但是离开LINQ几年之后,理解为什么会这样做会很好。

不工作我的意思是选项2给出了这个例外:

  

“System.NotSupportedException”类型的第一次机会异常   发生在EntityFramework.SqlServer.dll

中      

其他信息:LINQ to Entities无法识别该方法   'System.Linq.IQueryable`1 [typename] GetAllClients()'方法,这个   方法无法转换为商店表达式。

客户端是存储在数据库中的数据类型。我正在为常用查询创建实体框架数据模型的方法,并且由于多租户设计具有由数据类型定义的安全访问,我想在数据访问级别进行过滤。

2 个答案:

答案 0 :(得分:7)

此处的问题是可查询层正在尝试将方法调用GetAllClients转换为查询,而不是使用GetAllClients的返回值作为查询源。语法上的欺骗,是的,但也完全是预期的。

这种情况正在发生,因为与IQueryable不同,IEnumerable对象实际上提供了可用于将(在这种情况下)转换为SQL的元代码。由于大多数C#方法没有SQL等价物,并且编译后的方法无法以相同的方式扫描它们的元序列,因此当这些框架遇到无法翻译的内容时,它们就会出错。

请注意,避免大部分问题的一种方法是避免使用Linq styntax,而是进行方法调用,这会稍微减少欺骗:

return GetAllClients()
    .Where(item => item.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)
    .FirstOrDefault();

答案 1 :(得分:2)

您正在从过滤方法中返回IQueryable<Client>。 C#编译器识别出这一点,并且自动将将return语句中的lambda表达式转换为它的表达式树表示。 (自动转换是IQueryable的整个延迟执行功能如何工作)。

在第一种情况return item from allItems中,你得到一个这样的表达式树:

Call: Queryable.Select(Constant: allItems, LambdaExpression: predicate)

在第二种情况return item from GetAllClients()中,你会得到这个:

Call: Queryable.Select(Call: GetAllClients, LambdaExpression: predicate)

请注意,Queryable.Select的第一个参数是不同的!在第二种情况下,编译器通过将其存储在表达式树中来延迟执行GetAllClients的调用。当该表达式树最终到达EF的SQL转换器时,EF不知道如何将对C#GetAllClients函数的调用更改为有效的SQL。