实体框架:为什么人们使用.AsEnumerable()和EF查询

时间:2018-02-10 17:28:42

标签: entity-framework

我从未在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;
    }
}

3 个答案:

答案 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对象在内存中完成。

AutoCompleteDataToArray都会执行(实现)结果,但会执行不同的数据结构。在您的示例中,既不需要实现 - 因为返回类型为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)

AsEnumerableAsQueryable之间的区别在于,枚举包含创建枚举数的所有信息。一旦你得到了枚举器,你可以要求第一个元素,如果有的话,你可以得到下一个元素。

Queryable不包含创建枚举数的信息。它包含ExpressionProviderProvider知道哪个进程必须执行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具有相同的值。相反,只选择您真正想要使用的数据