作为 ORM,EFCore 可以让程序员从 SQL 语句中解放出来。那么它背后的运行机制是如何的呢?在 EF 之前,.NET 程序员访问数据库的一个重要的工具就是 ADO.NET,不过 ADO.NET 并不是 ORM,而是提供了诸如 Connection、Command、DataReader、DataProvider 的类来提供数据库接口, 程序员仍然需要自己编写 SQL 语句,并交给 ADO.NET 去执行。

EFCore 在底层最终也是通过 ADO.NET 来操作数据库的。当然,ADO.NET 本身也需要数据库系统去适配的,具体来说,需要为每个数据库系统编写特定的 ADO.NET Provider,例如 MySQL 数据库需要用 MySqlConnection、MySqlCommand、MySqlDataReader 这些类来操作。所以 EFCore 支持的数据库,在底层里也必须被 ADO.NET 支持。

在 EFCore 将 SQL 语句交给 ADO.NET 前,还有很多东西要做。最重要的就是将程序员编写的 C# 代码翻译为 SQL 语句,这个翻译过程不是一蹴而就的,主要分成两步,第一步是解析 C# 代码并将其转成一个抽象语法树(Abstract Syntax Tree,AST),第二步是将抽象语法树转为具体的 SQL 语句。

其中第一步是由 EFCore 核心完成的,因为这一步和具体使用哪个数据库没有关系。第二步则是与数据库有关的,因为每个数据库的语法是不一样的,支持的 SQL 函数也不同,所以这部分需要每个数据库去适配。于是微软提供了一种插件式平台,即为每个数据库编写 EntityFrameworkCore Provider,只要各个数据库系统按照 EFCore 标准去实现就可以了。目前 EFCore 支持绝大部分主流数据库。

用一张图来表示的话:

因此,不同的数据库,生成的 SQL 语句自然不同,这取决于该数据库的 EFCore Provider,并且不同的数据库所支持的函数也不同,可能有的 C# 代码在一个数据库系统中可以成功翻译,但在另外一个数据库系统中却翻译失败,这个时候就会出现运行时错误。

 MySQL 数据库可以选择开源的 Pomelo.EntityFrameworkCore.MySql 包,这是柚子基金会开发的,虽然不是 MySQL 官方的但是当前最多人用的。

EFCore 中是在 DbContext 类中配置数据库系统的连接的,使用 MySQL 的一个例子:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    string connStr = @"server=localhost;database=Test;user=root;password=root";
    base.OnConfiguring(optionsBuilder);
    optionsBuilder.UseMySql(connStr, new MySqlServerVersion(new Version(8, 0, 30)));
    optionsBuilder.UseLoggerFactory(logger);
    //optionsBuilder.LogTo(s => Console.WriteLine(s));

}

下面这段 C# 代码及 EFCore 翻译的 SQL 语句如下:

using (MyDbContext ctx = new MyDbContext())
{
    var books = ctx.Books.Where(e => e.Price > 50).OrderBy(e => e.Title);
    Console.WriteLine(books.Count());
}
SELECT COUNT(*)
FROM `T_Books` AS `t`
WHERE `t`.`Price` > 50.0