查询的原理

Entity Framework Core 使用语言集成查询(Language Integrate Query,LINQ)来从数据库中查询数据。LINQ 允许你使用 C#(或者你选择的其他 .NET 语言,比如 VB.NET)来基于你的派生上下文和实体类型编写强类型查询。

查询的生命周期

以下是对每个查询过程的高级概述。

  1. LINQ 查询通过 Entity Framework Core 处理以构建即将传递给数据库提供程序的表示。

    • 处理结果会被缓存,这样的话就不用在每次查询时都进行处理
  2. 上述处理结果会被传递给数据库提供程序

    • 数据库提供程序识别查询的哪些部分可以在数据库中评估
    • 识别到的查询的这些部分会被翻译为数据库特定的查询语言(例如,关系数据库的 SQL)
    • 一个或多个查询被发送到数据库,然后返回结果集(结果来自数据库,不是实体实例)
  3. 对于结果集中的每个项

    • 如果这是跟踪查询,EF 会验证数据是否表示上下文实例的变更跟踪器中的实体
      • 如果是,则返回已有的实体
      • 如果不是,则创建一个新实体,设置变更跟踪,然后返回这个新的实体。
    • 如果这是不跟踪查询,EF 会验证数据是否表示当前查询的结果集中的其他实体
      • 如果是,则返回已有的实体[1]
      • 如果不是,则创建和返回新实体

[1] 无跟踪查询使用弱引用来保持跟踪已返回的实体实例。如果具有相同标识的之前的结果超出作用范围,则 GC 会执行回收,你可能会获得一个新的实体实例。

查询时执行

在你调用 LINQ 操作的时候,你只是简单地在内存中构建了查询的表示。查询只会在使用结果的时候被发送到数据库。

触发查询被发送到数据库的最通用操作包括:

  • for 循环中对结果集进行迭代
  • 使用类似于 ToListToArraySingleCount 等的操作
  • 将查询结果绑定到 UI

警告

务必要验证用户输入: 尽管 EF 提供了 SQL 注入攻击防护,它并不做任何常规的输入验证。另外,如果传值给 API 、使用 LINQ 查询、给实体属性赋值等等来自不可信的源,那么就应该针对你的每个应用程序需求进行合适的验证。这包括用户用于动态构造查询的任何输入。即便是使用 LINQ,如果你接受用户输入来构建表达式,就需要确保只能构造预期的表达式。