Entity SQL (eSQL) 教程
Entity SQL (eSQL) 是一种针对实体框架设计的查询语言,它类似于传统的 SQL,但专门用于查询概念模型(EDM - Entity Data Model),而不是直接查询数据库表。

目录
- 什么是 Entity SQL?
- eSQL vs. 传统 SQL vs. LINQ to Entities
- 核心概念
- 概念模型
EDM(Entity Data Model)ENTITY关键字
- 基本语法
SELECT子句FROM子句WHERE子句GROUP BY和HAVING子句
- eSQL 特有的高级功能
- 导航属性
- 集合操作
- 行构造函数
TREAT操作符OfType函数
- 如何执行 eSQL 查询
- 使用
EntityCommand和EntityDataReader - 使用
ObjectQuery<T>(更推荐的方式)
- 使用
- 一个完整的示例
- eSQL 的优缺点与未来
什么是 Entity SQL?
Entity SQL 是一种面向对象的查询语言,它操作的是实体和它们之间的关系,而不是数据库的表和列,当你使用 eSQL 时,你是在与概念模型进行交互,而不是底层的数据库结构。
核心思想: 将查询逻辑与特定的数据库实现(如 SQL Server, Oracle)和物理数据结构(表、列)分离开来。
eSQL vs. 传统 SQL vs. LINQ to Entities
| 特性 | 传统 SQL | Entity SQL (eSQL) | LINQ to Entities |
|---|---|---|---|
| 语言类型 | 声明式字符串 | 声明式字符串 | 强类型语言集成查询 |
| 编译时检查 | 无,运行时才报错 | 无,运行时才报错 | 有,编译时检查语法和类型 |
| IntelliSense | 不支持 | 不支持 | 支持 |
| 参数化 | 使用 @param 语法 |
使用 @param 语法 |
使用强类型参数 |
| 主要用途 | 直接查询数据库 | 动态构建查询、复杂查询 | 应用程序中所有数据查询的首选 |
| 灵活性 | 高,但易出错 | 高,适合动态查询 | 高,但灵活性略低于字符串拼接 |
| 推荐度 | (EF中) 不推荐 | 较少使用,特定场景 | 强烈推荐 |
在 Entity Framework 中,LINQ to Entities 是首选,因为它提供了编译时安全、类型检查和更好的开发体验,eSQL 主要在需要动态构建查询的场景下才有用武之地。
核心概念
概念模型
eSQL 查询的基础是概念模型,它是一个 .edmx 文件,描述了你的实体(如 Person, Order)、它们的属性(如 Person.Name, Order.Date)以及它们之间的关系(如 Person.Orders)。

EDM (Entity Data Model)
.edmx 文件包含了三个部分:
- CSDL (Conceptual Schema Definition Language): 定义了实体、属性和关系,这是 eSQL 直接操作的部分。
- SSDL (Storage Schema Definition Language): 定义了数据库表、列和关系。
- MSL (Mapping Schema Language): 定义了 CSDL 和 SSDL 之间的映射关系。
ENTITY 关键字
在 eSQL 中,当你引用一个实体时,通常需要使用 ENTITY 关键字来明确表示你正在操作的是概念模型中的实体,而不是数据库对象。
查询一个名为 Person 的实体:
-- eSQL SELECT VALUE p FROM Entities.Person AS p
Entities 是 CSDL 中定义的命名空间。

基本语法
eSQL 的基本语法与 SQL 非常相似,但有一些关键区别。
SELECT 子句
使用 SELECT VALUE 来指定查询返回的结果。VALUE 关键字表示结果集合中的每个元素就是一个实体或标量值,而不是一个匿名的记录包装器。
-- 返回所有 Person 实体 SELECT VALUE p FROM Entities.Person AS p -- 返回 Person 的 Name 属性(标量值) SELECT p.Name FROM Entities.Person AS p
FROM 子句
指定查询的数据源,数据源是概念模型中的实体集合。
SELECT VALUE p FROM Entities.Person AS p
AS p 是一个可选的别名,方便在后续子句中引用。
WHERE 子句
用于过滤结果,语法与 SQL 类似。
SELECT VALUE p FROM Entities.Person AS p WHERE p.Age > 30 AND p.Name LIKE 'John%'
GROUP BY 和 HAVING 子句
与 SQL 类似,用于聚合和分组。
SELECT p.City, COUNT(p) AS PersonCount FROM Entities.Person AS p GROUP BY p.City HAVING COUNT(p) > 10
eSQL 特有的高级功能
eSQL 的强大之处在于它提供了许多传统 SQL 所不具备的、面向对象的特性。
导航属性
这是 eSQL 的核心优势,你可以直接通过导航属性访问关联实体,而无需 JOIN。
假设 Person 和 Order 是一对多关系,Person 有一个 Orders 导航属性。
-- 查找所有订单数量超过5的客户的姓名 SELECT VALUE p.Name FROM Entities.Person AS p WHERE p.Orders.Count > 5
这相当于 SQL 中的 JOIN 和 GROUP BY,但 eSQL 的语法更简洁直观。
集合操作
eSQL 支持 UNION, INTERSECT, EXCEPT。
-- 合并两个查询结果,并去除重复项 SELECT VALUE p FROM Entities.Person AS p WHERE p.City = 'London' UNION SELECT VALUE p FROM Entities.Person AS p WHERE p.City = 'Paris'
行构造函数
使用 ROW 关键字可以创建包含多个属性的新对象,非常适合 SELECT 子句返回匿名类型。
SELECT ROW(p.Name AS FullName, p.Age) FROM Entities.Person AS p
这将返回一个包含 FullName 和 Age 属性的对象集合。
TREAT 操作符
TREAT 用于将一个实体视为其派生类型,当你有一个基类实体(如 Person)和一些派生类(如 Employee, Customer)时,TREAT 非常有用。
假设 Employee 继承自 Person。
-- 查询所有员工,并访问其特有的 'Salary' 属性 SELECT VALUE e.Salary FROM TREAT(Entities.Person AS Employee) AS e WHERE e IS OF (Employee)
e IS OF (Employee) 确保我们只处理 Employee 类型的实例。
OfType 函数
OfType 是 TREAT 的一个更简洁的替代品,用于从集合中筛选特定类型的实体。
-- 查询所有员工 SELECT VALUE e.Salary FROM Entities.Person.OfType(Employee) AS e
如何执行 eSQL 查询
在代码中执行 eSQL 查询主要有两种方式。
使用 EntityCommand 和 EntityDataReader (底层方式)
这种方式类似于传统的 SqlCommand 和 SqlDataReader。
using (var context = new MyDbContext())
{
string eSqlQuery = "SELECT VALUE p FROM Entities.Person AS p WHERE p.Name = @name";
using (EntityCommand cmd = context.Connection.CreateCommand())
{
cmd.CommandText = eSqlQuery;
cmd.Parameters.Add(new EntityParameter("name", DbType.String) { Value = "Alice" });
using (EntityDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
// 注意:这里需要手动将数据映射到对象
string name = reader["Name"].ToString();
int age = Convert.ToInt32(reader["Age"]);
Console.WriteLine($"Name: {name}, Age: {age}");
}
}
}
}
使用 ObjectQuery<T> (推荐方式)
ObjectQuery<T> 是一个更高级、更方便的类,它可以直接执行 eSQL 查询并自动将结果映射到指定的实体类型 T。
using (var context = new MyDbContext())
{
string eSqlQuery = "SELECT VALUE p FROM Entities.Person AS p WHERE p.Name = @name";
// 创建 ObjectQuery
ObjectQuery<Person> query = new ObjectQuery<Person>(eSqlQuery, context);
// 添加参数
query.Parameters.Add(new ObjectParameter("name", "Alice"));
// 执行查询并获取结果集合
List<Person> people = query.ToList();
foreach (var person in people)
{
// 结果已经是 Person 对象,无需手动映射
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
}
}
一个完整的示例
假设我们有一个简单的模型:Person 和 Order。
定义模型 (CSDL 部分)
<EntityType Name="Person"> <Key><PropertyRef Name="PersonId"/></Key> <Property Name="PersonId" Type="Int32" Nullable="false"/> <Property Name="Name" Type="String" MaxLength="50" Nullable="false"/> <NavigationProperty Name="Orders" Relationship="MyModel.FK_Order_Person" FromRole="Person" ToRole="Order" Multiplicity="*"/> </EntityType> <EntityType Name="Order"> <Key><PropertyRef Name="OrderId"/></Key> <Property Name="OrderId" Type="Int32" Nullable="false"/> <Property Name="OrderDate" Type="DateTime" Nullable="false"/> <NavigationProperty Name="Person" Relationship="MyModel.FK_Order_Person" FromRole="Order" ToRole="Person" Multiplicity="1"/> </EntityType>
执行 eSQL 查询 (C#)
// 假设 MyDbContext 已正确配置
using (var context = new MyDbContext())
{
// 查询:找到所有下过订单的客户姓名和订单数量
string eSqlQuery = @"
SELECT
p.Name AS CustomerName,
p.Orders.Count AS OrderCount
FROM Entities.Person AS p
WHERE p.Orders.Count > 0";
try
{
ObjectQuery<DbDataRecord> query = new ObjectQuery<DbDataRecord>(eSqlQuery, context);
foreach (DbDataRecord record in query)
{
string customerName = record["CustomerName"].ToString();
int orderCount = Convert.ToInt32(record["OrderCount"]);
Console.WriteLine($"Customer: {customerName}, Orders: {orderCount}");
}
}
catch (EntityException ex)
{
Console.WriteLine("eSQL Query Error: " + ex.Message);
}
}
这个示例展示了如何使用导航属性 p.Orders.Count 来轻松获取关联实体的数量,而无需编写复杂的 JOIN。
eSQL 的优缺点与未来
优点
- 与数据库无关:查询基于概念模型,更换数据库后(理论上)无需修改查询。
- 强大的面向对象特性:如导航属性、
TREAT、OfType等,使查询更符合对象模型。 - 适合动态查询:因为它是字符串,可以在运行时动态构建复杂的查询逻辑。
缺点
- 无编译时检查:eSQL 是字符串,如果写错了属性名或语法,只有在运行时才会报错。
- 缺乏 IntelliSense:在编写查询时,IDE 无法提供智能提示,容易出错。
- 学习曲线:对于习惯了 LINQ 的开发者来说,eSQL 的字符串形式显得笨重。
- 已被边缘化:微软官方推荐使用 LINQ to Entities,eSQL 的文档和支持都较少。
未来
eSQL 已经不是一个主流技术,在 Entity Framework 6 和 Entity Framework Core 中,LINQ to Entities 是绝对的主角,除非你有非常特殊的需求(需要从外部来源动态生成极其复杂的查询),否则应该优先选择 LINQ。
| 特性 | |
|---|---|
| 是什么 | 一种面向概念模型的、类 SQL 的查询语言。 |
| 核心优势 | 导航属性、与数据库无关、适合动态查询。 |
| 主要劣势 | 无编译时检查、无 IntelliSense、易出错。 |
| 与 LINQ 的关系 | LINQ to Entities 是 EF 的首选,eSQL 是一个补充,用于特定场景。 |
| 何时使用 | 当你需要在运行时动态构建无法用 LINQ 表示的复杂查询时。 |
| 学习建议 | 先精通 LINQ to Entities,再了解 eSQL 作为补充知识。 |
希望这份教程能帮助你理解 Entity SQL!
