我正在使用辅助方法根据用户访问权限预过滤我的所有查询。
假设方法签名为:
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()'方法,这个 方法无法转换为商店表达式。
客户端是存储在数据库中的数据类型。我正在为常用查询创建实体框架数据模型的方法,并且由于多租户设计具有由数据类型定义的安全访问,我想在数据访问级别进行过滤。
答案 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。