我从未在EntityFramework查询中使用.AsEnumerable()
。
请参阅下面的示例并告诉我他们为什么在.AsEnumerable()
之前使用Select
?
他们不能直接使用Select
吗?
请在下面的查询中告诉我使用.AsEnumerable()
的原因。
为什么他们使用.ToArray()
代替.Tolist()
?
private IEnumerable<AutoCompleteData> GetAutoCompleteData(string searchTerm)
{
using (var context = new AdventureWorksEntities())
{
var results = context.Products
.Include("ProductSubcategory")
.Where(p => p.Name.Contains(searchTerm)
&& p.DiscontinuedDate == null)
.AsEnumerable()
.Select(p => new AutoCompleteData
{
Id = p.ProductID,
Text = BuildAutoCompleteText(p)
})
.ToArray();
return results;
}
}
答案 0 :(得分:2)
并非所有Select
个投影,Where
谓词和Aggregations
都可以从C#表达式转换为本机数据库查询 - 在您的情况下,完整的LINQ表达式会尝试构造一个{{ 1}}类使用自定义函数AutoCompleteData
来设置其中一个属性 - 这不能简单地转换为SQL等本机数据库代码。
在您的情况下,BuildAutoCompleteText
用于终止将在SQL中执行的工作,之后将在SQL中执行。
即。
AsEnumerable
将在SQL中执行,大致为.Include("ProductSubcategory")
.Where(p => p.Name.Contains(searchTerm)
&& p.DiscontinuedDate == null)
到JOIN
,以及从ProductSubcategory
翻译的WHERE
谓词,例如:
Products
Product.Name LIKE '%' + @SearchTerm + '%' AND Product.DiscontinuedDate IS NULL
之后的所有工作(即将结果投影到AsEnumerable
个对象)将使用LINQ to对象在内存中完成。
AutoCompleteData
和ToArray
都会执行(实现)结果,但会执行不同的数据结构。在您的示例中,既不需要实现 - 因为返回类型为ToList
- 函数的调用者可能会执行IEnumerable<AutoCompleteData>
或.Any()
,这会导致完全实现浪费 - 我建议您删除完全First()
- 由于.ToArray()
语句控制SQL生命周期受using
实现的保护,因此此处的连接生命周期没有问题。
答案 1 :(得分:1)
告诉我在下面的查询中使用.AsEnumerable()的意图吗?
在此特定示例中,AsEnumerable()
用于将数据带回客户端,因为EF不知道如何将BuildAutoCompleteText()
映射到SQL查询。
他们可以直接使用select .....不是吗?
不,除非您在SQL Server上定义自定义函数BuildAutoCompleteText
并让EF知道该函数。
为什么他们使用.ToArray();而不是Tolist()?
在这种情况下,实现IEnumerable<T>
答案 2 :(得分:1)
AsEnumerable
和AsQueryable
之间的区别在于,枚举包含创建枚举数的所有信息。一旦你得到了枚举器,你可以要求第一个元素,如果有的话,你可以得到下一个元素。
Queryable
不包含创建枚举数的信息。它包含Expression
和Provider
。 Provider
知道哪个进程必须执行Expression
以及此进程使用哪种语言。通常,其他进程是数据库管理系统,语言是SQL。
Queryable.Select(...)的结果仍然是IQueryable
,这意味着查询尚未执行。 Select函数仅更改了Expression
。
只有当您通过调用GetEnumerator()
显式地或通过调用foreach
或者ToList()
之类的非延迟执行函数(ToDictionary()
,{{1)明确要求Enumerator时} {},FirstOrDefault()
,Sum()
,Provider
会将表达式转换为执行过程理解并执行查询的格式。将数据传输到本地进程后,就会创建枚举器。
唉,有时您想在查询中调用自己的函数。 SQL不知道这些函数,因此Provider无法将此类Expressions
转换为SQL。事实上,DbContext的提供者甚至不知道所有Linq函数。见supported and unsupported Linq methods
这是您使用AsEnumerable()
的时刻。如果您要求使用枚举器(例如,在foreach
中),则提供商会将表达式翻译为AsEnumerable
;将其发送到执行过程并将所有数据传输到本地进程。之后,查询将为AsEnumerable
:LINQ的其余部分将在本地内存中执行,因此可以调用本地函数。
您当然可以使用ToList()将所有数据提取到本地内存并在此之后继续使用linq。但如果您只想要第一个元素或其他所有元素,那将是一种浪费。
这让我想到最后一句话:将数据从DBMS传输到本地内存是较慢的部分之一。尝试将此传输限制为仅实际使用的数据。
例如:如果教师和他的学生之间存在一对多关系,请不要取教师和他的学生,因为您将多次运送Student.TeacherId
,他们都会与Teacher.Id
具有相同的值。相反,只选择您真正想要使用的数据