【问题标题】:Analyzing queries by Entity Framework通过实体框架分析查询
【发布时间】:2016-04-02 17:06:27
【问题描述】:

我正在学习实体框架,我需要一些解释。

假设我已经创建了一些多对多关系,因此我们有表StudentsCourses,并且每种类型的实体可以有许多其他类型的实体。

接下来,我有一个网格视图,我想在其中显示包含两列描述学生姓名和分配给他的所有课程的表格。为此,在我的WebFormPage_Load 事件中,我有以下代码:

protected void Page_Load(object sender, EventArgs e)
{
    MyModel dbContext = new MyModel();
    GridView1.DataSource = from student in dbContext.Students
                                .Include("Courses").ToList()
                           select new
                                   {
                                       Name = student.StudentName,
                                       Courses = string.Join(",", student.Courses.Select(c => c.CourseName))
                                   };
    GridView1.DataBind();
}

现在它可以工作了,但请注意,Entity Framework 并没有真正分析查询,因为行:

GridView1.DataSource = from student in dbContext.Students.Include("Courses").ToList()

使此查询由 LINQ to Objects 提供程序分析。如果我不使用ToList() 方法而是我想写:

var query = from student in dbContext.Students.Include("Courses")
            select new { ... };
GridView1.DataSource = query.ToList();
GridView1.DataBind();

那么我有一个例外,Entity Framework 无法识别 Join() 方法。我可以接受,但猜想如果查询由 EF 的引擎分析,应用程序会运行得更快,对吗?

如果是这样,我如何重写代码,将字符串分配给我的匿名类型的 Courses 属性以获得相同的结果?

【问题讨论】:

    标签: c# sql asp.net entity-framework linq


    【解决方案1】:

    到目前为止,您所做的唯一真正的问题是,如果您想应用过滤,它将在调用ToList 之后应用,这样就完成了在记忆中。 我会将您的查询分为两部分。第一部分将从数据库中读取数据(并允许您包含where 子句),而第二部分会将对象映射到格式中。

    protected void Page_Load(object sender, EventArgs e)
    {
        using (var dbContext = new MyModel())
        {
            var students = (from student in dbContext.Students
                            /*optional where clause*/
                            select new
                                   {
                                       Name = student.StudentName,
                                       Courses = student.Courses.Select(c => c.CourseName)
                                   }).ToList();
            GridView1.DataSource = students.Select(s => new { s.Name, Courses = String.Join(",", s.Courses)}).ToList();
            GridView1.DataBind();
        }
    }
    

    值得一提的是,您可以使用 Aggregate 在 EF 中完全重新创建它,但我认为没有必要。

    【讨论】:

    • 我认为.Include("Courses") 在这种情况下是多余的
    • 非常正确 - 通过选择子句中的课程名称调用ToList,这是第一次数据库调用,然后是包含调用不是必需的。
    • @pawel 你是用你的例子还是我的例子得到 NRE?
    • @pawel 是的,include 的目的是特别急切地加载实体,但在我的示例中,课程的选择发生在实体加载之前。事实上,我们从不加载实体,只加载匿名类型。
    【解决方案2】:

    关于为什么Join() 不起作用。 代码

    var query = from student in dbContext.Students.Include("Courses") select new { ... };
    

    返回IQueryable。此时您的数据尚未具体化(= 没有调用数据库来获取实体)。这意味着当您稍后调用ToList()(=物化您的实体)时,查询中包含的所有内容都将转换为 SQL。 SQL 对string.Join() 一无所知,这就是它抱怨的原因。您的第一个示例之所以有效,是因为 LinqToObjects 是在 C# 中执行的,而不是 SQL(因此,它知道如何处理 string.Join()

    更多关于您可以和不能包含在查询中的内容:

    Supported and Unsupported LINQ Methods (LINQ to Entities)

    CLR Method to Canonical Function Mapping

    更新:

    虽然这不是问题的一部分,但值得一提的是,您可能需要为数据实现分页。您不太可能希望一次在一个数据网格中显示成百上千的项目。搜索Take()

    【讨论】:

    • 补充一点:处理这个问题的正确方法是在一个查询中完成所有可以在 SQL 中完成的部分(包括 where 条件),然后是 AsEnumrable(),然后你可以链接下一组 Linq 语句将在内存中发生。
    猜你喜欢
    • 2012-01-06
    • 2018-09-26
    • 1970-01-01
    • 2020-10-06
    • 1970-01-01
    • 1970-01-01
    • 2021-12-25
    • 2013-03-17
    • 2014-02-24
    相关资源
    最近更新 更多