
一、项目介绍
Entity Framework Core(简称 EF Core)是 .NET 平台上现代化的对象-关系映射(ORM)框架,由微软官方维护,采用 MIT 许可证开源,源码托管在 dotnet/efcore 仓库,属于 .NET Foundation 项目。它的核心作用是让开发者用面向对象的方式(C# 实体类与 LINQ 查询)来操作数据库,而不必手写大量 SQL 样板代码,从而把业务模型与数据访问细节解耦。
EF Core 支持的能力包括:LINQ 查询、变更跟踪(change tracking)、实体新增/更新/删除的统一提交、以及数据库架构迁移(schema migrations)。它通过提供程序(provider)插件架构对接多种数据库,官方与社区提供的提供程序覆盖 SQL Server、Azure SQL Database、SQLite、Azure Cosmos DB、MariaDB、MySQL、PostgreSQL 等,其他数据库的提供程序列表见官方文档。同一个业务模型可以在这些数据库之间切换,只需更换提供程序包与连接字符串。
该仓库同时托管另一个独立项目 Microsoft.Data.Sqlite,这是一个轻量级的 SQLite ADO.NET 提供程序,可以独立于 EF Core 使用,也可以被 EF Core 的 SQLite 提供程序在底层依赖。EF Core 的 SQLite 提供程序正是构建在这个库之上。
与旧版 EF6(仅能在 Windows/.NET Framework 上运行)不同,EF Core 是完全跨平台、轻量、可组合的,既可以在 ASP.NET Core 后端、控制台工具里使用,也可以跑在云原生容器、WPF/WinForms 桌面应用乃至移动场景中。它与 ASP.NET Core 的依赖注入系统天然集成,是 .NET 数据访问层的事实标准。
在典型的企业应用中,EF Core 负责把领域模型(POCO 实体类)映射到关系数据库表:通过约定优先(convention over configuration)自动识别主键、外键与导航属性,也可以用数据注解或 Fluent API 精细控制列类型、长度、索引与关系删除行为。它的变更跟踪机制会在一次上下文中记录实体的原始值与当前值,提交时只生成必要的 UPDATE 语句;对于只读查询,还可以通过 AsNoTracking 跳过跟踪以降低开销。LINQ 提供程序会把强类型查询翻译为参数化 SQL,既避免了拼接 SQL 带来的注入风险,又让编译期就能发现拼写与类型错误。

二、项目结构
dotnet/efcore 仓库以 src/ 为核心,按功能划分目录。其中 Microsoft.EntityFrameworkCore 主程序集承载核心抽象:DbContext(工作单元)、DbSet<TEntity>(实体集)、变更跟踪器、模型构建器(ModelBuilder)与迁移基础设施。Query/ 目录承载 LINQ 表达式到 SQL 翻译的整套管线;Metadata/ 目录承载实体模型、关系、键、索引等元数据的构建;Storage/ 目录承载底层的数据库连接与命令执行。
提供程序目录按数据库拆分:SqlServer/ 承载 SQL Server 提供程序;Sqlite/ 承载 SQLite 提供程序(底层依赖 Microsoft.Data.Sqlite);Cosmos/ 承载 Azure Cosmos DB 提供程序;InMemory/ 承载用于单元测试的内存数据库提供程序。migrations/ 目录承载迁移生成、模型快照与 SQL 脚本生成逻辑。test/ 目录包含海量功能测试,验证每个提供程序在真实数据库上的行为。
docs/ 目录收录了从源码构建代码(getting-and-building-the-code.md)、日常构建说明、安全说明等文档;.github/ 目录则包含贡献指南、支持说明与行为准则。对于普通使用者,你通过 NuGet 引用的 Microsoft.EntityFrameworkCore.* 包就是从这个仓库预编译发布的;只有想参与框架开发、调试翻译器或贡献提供程序时,才需要从源码构建。
从使用视角看,仓库产出的关键程序集职责清晰:Microsoft.EntityFrameworkCore 是抽象与查询管线;Microsoft.EntityFrameworkCore.Relational 是所有关系型提供程序共用的 SQL 生成与迁移底座;各数据库提供程序包则在其之上补齐方言差异(如自增列、分页 TOP/LIMIT、函数映射)。理解这一分层,有助于在遇到翻译问题时快速定位是核心 bug 还是提供程序差异。

三、版本与环境要求
使用 EF Core 的前提是安装 .NET SDK。EF Core 的主版本与 .NET 主版本大致对齐:EF Core 8 对应 .NET 8、EF Core 9 对应 .NET 9,以此类推;稳定版本发布在 NuGet 上,README 中的 badge 会实时显示最新稳定版与预览版号。通过 dotnet add package 安装时若不指定版本,默认拉取最新稳定版;使用 --version 选项可以安装预览版或指定版本。
运行时方面,EF Core 可以运行在支持 .NET 标准库的各类目标框架上,包括 .NET(Core)、.NET Framework、以及 Xamarin/Mono 等;但新特性通常优先面向最新的 .NET LTS 版本,生产环境建议使用 .NET LTS 以获得长期补丁支持。不同数据库提供程序对运行时的要求略有差异,例如 Cosmos 提供程序依赖 Azure SDK,SQLite 提供程序依赖 SQLite 原生库(EF Core 已自带所需的原生二进制)。
数据库方面,使用前需准备好对应数据库实例:SQL Server / Azure SQL、PostgreSQL、MySQL/MariaDB、Cosmos DB 或本地 SQLite 文件。SQLite 无需安装独立服务,一个本地 .db 文件即可;PostgreSQL、MySQL 等则需自行启动数据库服务并准备连接字符串。命令行工具 dotnet-ef(迁移与脚手架工具)需要通过 dotnet tool install --global dotnet-ef 单独安装,它是独立的全局工具,不在运行时包里。
需要特别注意包版本的对齐:Microsoft.EntityFrameworkCore、Microsoft.EntityFrameworkCore.Design、dotnet-ef 工具三者的主版本号应保持一致,否则在添加迁移或 scaffold 时容易出现程序集加载冲突或行为异常。使用预览版时,除了加 --version 指定包版本,还可能需要在 NuGet 配置中启用预发布流。日常构建反馈则可以采用 README 推荐的每日构建(daily builds),它们包含最新特性与缺陷修复,比正式发布超前不少。
四、本地部署步骤
方式一:通过 NuGet 引用提供程序(最常用)。先创建或进入一个 .NET 项目,然后根据目标数据库安装对应的提供程序包。README 给出的标准命令为:
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Cosmos若使用 PostgreSQL 或 MySQL,则安装社区提供程序,例如 Npgsql.EntityFrameworkCore.PostgreSQL、Pomelo.EntityFrameworkCore.MySql。安装后定义一个继承 DbContext 的上下文类与实体类:
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs => Set<Blog>();
public DbSet<Post> Posts => Set<Post>();
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite("Data Source=Blogs.db");
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; } = new();
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
}方式二:在 ASP.NET Core 中通过依赖注入注册。在 Program.cs 中注册 DbContext,是 Web 项目的标准做法:
builder.Services.AddDbContext<BloggingContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("Default")));之后通过构造函数把 BloggingContext 注入控制器或最小 API 处理器,EF Core 会为每个请求创建一个作用域内的上下文实例。
方式三:使用迁移管理数据库结构。安装全局工具后,可在命令行生成并应用迁移:
dotnet tool install --global dotnet-ef
dotnet ef migrations add InitialCreate
dotnet ef database update第一条命令把实体模型的差异生成为迁移类;第二条命令把迁移真正应用到数据库,建表、加列、建索引。也可以用 dotnet ef migrations script 生成纯 SQL 脚本,供 DBA 在生产库执行。
方式四:从源码构建(贡献者场景)。按 README 指引,参考 docs/getting-and-building-the-code.md,先 clone 仓库,再用 build.cmd / build.sh 还原并构建,产物可打包为本地 NuGet 包替换官方版本进行调试。普通使用者无需此步骤。
补充说明上下文的生命周期:DbContext 不是线程安全的实例,设计上"一个工作单元一个上下文"。在 ASP.NET Core 中由 DI 容器按请求创建、请求结束自动释放;在控制台或后台任务中,应使用 using var db = new BloggingContext() 显式控制作用域,切勿把一个上下文长期复用为单例,否则会出现连接占用、变更跟踪膨胀与并发错误。
五、浏览器端部署
EF Core 是纯数据访问库,本身没有界面,所谓浏览器端部署指的是它所服务的 Web 应用通过浏览器被访问。当 EF Core 被集成到 ASP.NET Core 应用后,你按常规方式启动 Web 应用(dotnet run 或部署到 IIS/Nginx/Docker),浏览器访问的是 Web 前端,EF Core 则在后端默默完成对象与数据库之间的映射。
首次启动 Web 应用时,建议在开发环境调用 db.Database.Migrate() 自动把迁移应用到开发库,或在启动日志中确认表已创建;生产环境则应通过 CI/CD 管道在发布流程中执行 dotnet ef database update,或由 DBA 执行迁移脚本。SQLite 场景下,数据库文件会出现在应用的工作目录,部署到 Linux 服务器时要确保该目录对运行用户可写,否则建库与写入会失败。
浏览器端能直接观察到 EF Core 行为的典型位置是 Swagger/OpenAPI 页面:那些返回 Blogs、Posts 的接口,背后正是 EF Core 在执行 LINQ 查询并把实体序列化为 JSON。若接口返回 500,多半是连接字符串、迁移未应用或提供程序包缺失,可先在服务器命令行直接运行应用复现异常。
对于追求性能的站点,还可以在生产环境启用敏感日志关闭(默认已关)、在查询侧用 ToListAsync/FirstOrDefaultAsync 异步化、并通过日志或 SimpleLogging 观察 EF Core 实际生成的 SQL。需要强调的是,EF Core 不会魔法般消除慢查询:N+1 问题(循环里逐条取关联数据)、缺失索引、对大表全量 AsNoTracking 扫描等仍需开发者主动通过 Include、Select 投影与索引设计来规避。
六、API 用法
README 给出的基础用法展示了增删改查的完整闭环:
using var db = new BloggingContext();
// 插入数据
db.Add(new Blog { Url = "http://blogs.msdn.com/adonet" });
db.SaveChanges();
// 查询
var blog = db.Blogs
.OrderBy(b => b.BlogId)
.First();
// 更新
blog.Url = "https://devblogs.microsoft.com/dotnet";
blog.Posts.Add(new Post { Title = "Hello World", Content = "I wrote an app using EF Core!" });
db.SaveChanges();
// 删除
db.Remove(blog);
db.SaveChanges();查询侧使用 LINQ 语法,EF Core 会把查询表达式翻译为对应数据库的 SQL:Where、OrderBy、Include(预加载关联实体)、Select、Any/Count 等都支持。SaveChanges() 是统一提交入口,它会扫描变更跟踪器中所有新增、修改、删除的实体,并在一个事务内批量写库。
底层若要直接用 ADO.NET(不经过 EF Core),可以使用同仓库的 Microsoft.Data.Sqlite:
dotnet add package Microsoft.Data.Sqliteusing var connection = new SqliteConnection("Data Source=Blogs.db");
connection.Open();
using var command = connection.CreateCommand();
command.CommandText = "SELECT Url FROM Blogs";
using var reader = command.ExecuteReader();
while (reader.Read())
{
var url = reader.GetString(0);
}更多配置( fluently API、数据注解、全局查询过滤器、拆分查询、NoTracking 只读优化)可查阅 EF Core 官方文档。官方同时提供按数据库拆分的教程,建议第一次上手时按 getting-started 走一遍"建模型、建上下文、迁移、增删改查"的完整闭环,再逐步引入并发令牌、全局查询过滤器、值转换等进阶能力。
七、常见问题 FAQ
问一:运行时找不到 UseSqlServer / UseNpgsql 方法?
答:说明只装了主包 Microsoft.EntityFrameworkCore 而没装对应提供程序包。EF Core 采用"核心 + 提供程序"分离设计,必须同时安装 Microsoft.EntityFrameworkCore.SqlServer、Npgsql.EntityFrameworkCore.PostgreSQL 等提供程序,并 using 对应的命名空间。
问二:dotnet ef 命令找不到?
答:dotnet-ef 是独立的全局工具,需要先执行 dotnet tool install --global dotnet-ef;若已安装但提示版本不匹配,用 dotnet tool update --global dotnet-ef 升级到与项目 EF Core 主版本一致的版本。
问三:查询时报"The connection string is not configured"或数据库文件被锁?
答:前者是 OnConfiguring / AddDbContext 中漏配 UseXxx 或连接字符串为空;后者常见于 SQLite,多半是同一 db 文件被多个进程打开,或工作目录不可写。生产环境建议改用服务型数据库(SQL Server/PostgreSQL),避免单文件锁竞争。
问四:修改实体字段后数据库表结构没变化?
答:EF Core 不会自动改表,需要新增迁移并应用:dotnet ef migrations add Xxx 后 dotnet ef database update;如果旧迁移已脏,可先 dotnet ef migrations remove 回退再重新添加。
问五:循环序列化导致 JSON 返回死循环?
答:这是因为实体之间的导航属性(Blog.Posts 与 Post.Blog)互相引用,被序列化器递归展开。应在 DTO 层投影(用 Select 把实体转成扁平 DTO),或在 JSON 配置中设置 ReferenceHandler.IgnoreCycles,而不是直接把 DbSet 实体返回给浏览器。
问六:要不要为每个仓库包一层 Repository 与 UnitOfWork?
答:DbContext 本身已经实现了工作单元模式,DbSet 也封装了仓储的常用查询能力。多数场景下直接注入 DbContext 即可,强行加一层抽象只会损失 LINQ 的强类型与组合能力;只有当你需要跨多种数据源、或希望对上层完全屏蔽 EF Core 时,才值得引入自定义仓储接口。
问七:EF Core 会不会自动处理并发冲突?
答:默认不会。要开启并发控制,需在实体上配置并发令牌(如一行版本号属性),EF Core 会在 UPDATE/DELETE 的 WHERE 子句中带上该令牌,提交时若发现已被他人修改则抛出 DbUpdateConcurrencyException,由业务侧决定重试或合并。
相关链接:官方仓库 · 官方文档 · NuGet 包 · 提供程序列表 · 从源码构建
说明:本文命令与包名均参照官方 README 整理,实际版本号请以 NuGet 与仓库 main 分支最新说明为准。生产环境建议同时开启详细错误日志与慢查询监控,以便及时发现翻译与索引问题。建议首次上手按官方 getting-started 完整跑通一遍建库与迁移流程后再投入业务。