.NET Entity Framework (EF) Core 完整教程

目录

  1. 什么是 EF Core?
  2. 为什么使用 EF Core?
  3. 核心概念
  4. 环境准备
  5. 实战教程:创建第一个 EF Core 应用
    • 步骤 1:创建项目
    • 步骤 2:安装 EF Core NuGet 包
    • 步骤 3:定义模型类
    • 步骤 4:创建数据库上下文
    • 步骤 5:配置数据库连接字符串
    • 步骤 6:使用 Code First 创建数据库
    • 步骤 7:进行 CRUD 操作
  6. 进阶主题
  7. 学习资源与最佳实践

什么是 EF Core?

Entity Framework (EF) Core 是微软官方的开源、跨平台、轻量级的 对象关系映射器

.net ef 教程
(图片来源网络,侵删)

它是一个数据访问技术,能够让你:

  • 用 C# 对象(模型类)来操作数据库,而不是手写复杂的 SQL 语句。
  • 自动将 C# 对象映射到数据库表,将对象的属性映射到表的列。
  • 将数据库查询的结果自动转换回 C# 对象

你可以把它想象成一个“翻译官”,在你的 C# 代码和 SQL 数据库之间进行沟通。

重要提示:我们现在通常说的 EF Core,是 EF 的重大重构版本,专门为 .NET Core 和 .NET 5+ 设计,性能更好,更现代化。


为什么使用 EF Core?

  • 提高开发效率:无需编写、维护和调试大量重复的 SQL 代码,专注于业务逻辑。
  • 代码可读性和可维护性:C# 代码比 SQL 语句更易于阅读和维护。
  • 减少错误:自动化的映射和查询减少了手写 SQL 可能带来的错误。
  • 数据库无关性:你可以通过更换数据库提供程序,轻松地将应用程序从 SQL Server 切换到 PostgreSQL、SQLite、MySQL 等,而无需修改业务逻辑代码。
  • 强大的功能:支持 LINQ 查询、变更跟踪、迁移、异步操作等高级特性。

核心概念

在学习使用之前,必须理解以下几个核心概念:

.net ef 教程
(图片来源网络,侵删)
概念 解释 类比
模型 一个普通的 C# 类(POCO),通常用 [Table][Column] 等特性来标注,代表数据库中的一张表。 设计图纸(表的结构)
数据库上下文 继承自 DbContext 的类,是 EF Core 的核心,它负责连接数据库,并包含 DbSet<T> 属性来表示模型对应的数据库表。 施工队(负责按图纸盖房子)
DbSet DbContext 中定义的属性,类型为 DbSet<T>T 是你的模型类,它代表数据库中的一个表,并提供了对数据进行增删改查的方法(如 Add(), Remove(), Find(), ToList())。 施工队手中的工具箱(操作表的方法)
迁移 EF Core 的一项功能,用于将你的模型类(代码)的变更同步到数据库结构中,它会自动生成创建/修改表的 SQL 脚本并执行。 版本控制系统(Git),记录数据库结构的变更历史
LINQ 语言集成查询,一种在 C# 中编写查询的强大语法,EF Core 会将 LINQ 查询表达式翻译成相应的 SQL 语句发送给数据库。 用自然语言向施工队下达指令(“给我找所有红色的砖”)

环境准备

  1. 安装 .NET SDK:从 .NET 官网 下载并安装最新的 .NET SDK。
  2. 选择一个 IDE
    • Visual Studio 2025 (推荐,社区版免费):安装时请勾选 “.NET 桌面开发” 或 “ASP.NET 和 Web 开发” 工作负载。
    • Visual Studio Code:配合 C# Dev Kit 扩展使用,非常轻量。
  3. 选择一个数据库:为了简单起见,我们本教程使用 SQLite,它是一个轻量级的文件数据库,无需安装服务器,非常适合学习和本地开发。

实战教程:创建第一个 EF Core 应用

我们将创建一个控制台应用程序,管理一个简单的“博客”系统,包含 BlogPost 两个实体。

步骤 1:创建项目

打开终端或命令提示符,运行以下命令:

# 创建一个新的控制台应用
dotnet new console -n EfCoreDemo
# 进入项目目录
cd EfCoreDemo

步骤 2:安装 EF Core NuGet 包

我们需要安装 EF Core 核心包和 SQLite 提供程序包。

# 安装 EF Core 核心包
dotnet add package Microsoft.EntityFrameworkCore
# 安装 SQLite 提供程序包
dotnet add package Microsoft.EntityFrameworkCore.Sqlite

步骤 3:定义模型类

在项目中创建一个名为 Models 的文件夹,并在其中创建两个类:Blog.csPost.cs

Models/Blog.cs

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace EfCoreDemo.Models
{
    // [Table("Blogs")] // 可选,如果不指定,表名默认为类名
    public class Blog
    {
        public int BlogId { get; set; } // 默认为主键
        [Required] // 数据库列不能为空
        [StringLength(100)] // 字符串最大长度为100
        public string Url { get; set; }
        // 一个博客可以有多篇博文
        public List<Post> Posts { get; set; }
    }
}

Models/Post.cs

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EfCoreDemo.Models
{
    public class Post
    {
        public int PostId { get; set; }
        [Required]
        public string Title { get; set; }
        public string Content { get; set; }
        // 外键,指向 Blog 表的 BlogId
        public int BlogId { get; set; }
        // 导航属性,一篇博文属于一个博客
        [ForeignKey("BlogId")]
        public Blog Blog { get; set; }
    }
}

步骤 4:创建数据库上下文

创建一个名为 Data 的文件夹,并在其中创建 BloggingDbContext.cs 文件。

Data/BloggingDbContext.cs

using Microsoft.EntityFrameworkCore;
using EfCoreDemo.Models;
namespace EfCoreDemo.Data
{
    public class BloggingDbContext : DbContext
    {
        // 这个构造函数是必需的,用于将配置选项(如连接字符串)传递给 DbContext
        public BloggingDbContext(DbContextOptions<BloggingDbContext> options) : base(options)
        {
        }
        // DbSet 属性代表数据库中的表
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }
    }
}

步骤 5:配置数据库连接字符串

打开 Program.cs 文件,我们将在这里配置应用和数据库上下文。

Program.cs

using Microsoft.EntityFrameworkCore;
using EfCoreDemo.Data;
using System.Linq;
// 1. 配置数据库连接字符串并创建 DbContext 实例
// 使用 SQLite 内存数据库,数据只存在于程序运行期间
var connectionString = "Data Source=:memory:"; 
var optionsBuilder = new DbContextOptionsBuilder<BloggingDbContext>();
optionsBuilder.UseSqlite(connectionString);
using var dbContext = new BloggingDbContext(optionsBuilder.Options);
// 2. 确保数据库被创建
// 如果数据库不存在,此方法会根据模型创建它
dbContext.Database.EnsureCreated();
Console.WriteLine("数据库已创建!");
// ... 接下来我们将在这里进行 CRUD 操作 ...

步骤 6:使用 Code First 创建数据库

在上一步的 Program.cs 中,我们已经调用了 dbContext.Database.EnsureCreated(),这行代码会检查数据库是否存在,如果不存在,就会根据我们定义的 BlogPost 模型自动创建 BlogsPosts 表。

你可以运行程序试试看,虽然不会有输出,但数据库(在内存中)确实已经被创建了。

步骤 7:进行 CRUD 操作

我们向 Program.csusing 代码块中添加具体的增删改查操作。

完整的 Program.cs

using Microsoft.EntityFrameworkCore;
using EfCoreDemo.Data;
using EfCoreDemo.Models;
using System.Linq;
// 1. 配置数据库连接字符串并创建 DbContext 实例
var connectionString = "Data Source=:memory:"; 
var optionsBuilder = new DbContextOptionsBuilder<BloggingDbContext>();
optionsBuilder.UseSqlite(connectionString);
using var dbContext = new BloggingDbContext(optionsBuilder.Options);
// 2. 确保数据库被创建
dbContext.Database.EnsureCreated();
Console.WriteLine("数据库已创建!");
// ============ C - Create (创建) ============
Console.WriteLine("\n--- 创建博客 ---");
var blog = new Blog { Url = "https://example.com" };
dbContext.Blogs.Add(blog); // 将新博客添加到 DbSet
dbContext.SaveChanges();    // 保存更改到数据库
Console.WriteLine($"已创建博客,ID: {blog.BlogId}");
// ============ R - Read (查询) ============
Console.WriteLine("\n--- 查询所有博客 ---");
var allBlogs = dbContext.Blogs.ToList(); // 将查询结果转换为 List
foreach (var b in allBlogs)
{
    Console.WriteLine($"博客 ID: {b.BlogId}, URL: {b.Url}");
}
// ============ U - Update (更新) ============
Console.WriteLine("\n--- 更新博客 URL ---");
var blogToUpdate = dbContext.Blogs.FirstOrDefault(b => b.BlogId == 1);
if (blogToUpdate != null)
{
    blogToUpdate.Url = "https://updated-example.com";
    dbContext.SaveChanges(); // 再次保存更改
    Console.WriteLine($"已更新博客 ID {blogToUpdate.BlogId} 的 URL 为 {blogToUpdate.Url}");
}
// ============ D - Delete (删除) ============
Console.WriteLine("\n--- 删除博客 ---");
var blogToDelete = dbContext.Blogs.FirstOrDefault(b => b.BlogId == 1);
if (blogToDelete != null)
{
    dbContext.Blogs.Remove(blogToDelete); // 从 DbSet 中移除
    dbContext.SaveChanges(); // 保存更改
    Console.WriteLine($"已删除博客 ID {blogToDelete.BlogId}");
}
Console.WriteLine("\n--- 再次查询所有博客 ---");
var remainingBlogs = dbContext.Blogs.ToList();
if (!remainingBlogs.Any())
{
    Console.WriteLine("数据库中没有博客了。");
}

运行程序,你将在控制台看到完整的输出,清晰地展示了数据库的创建和数据的增删改查过程。


进阶主题

当你掌握了基础后,可以继续学习以下内容:

  • 数据库迁移:使用 dotnet ef 命令行工具来管理数据库结构的版本,这是生产环境中的标准做法。
    • dotnet ef migrations add MyNewMigration - 创建一个新的迁移。
    • dotnet ef database update - 将迁移应用到数据库。
  • 关系配置:使用 Fluent API 在 DbContextOnModelCreating 方法中更灵活地配置实体间的关系(一对一、一对多、多对多)。
  • 查询:深入学习 LINQ to Entities,包括过滤、排序、分组、聚合函数等。
  • 异步操作:使用 asyncawait 关键字执行数据库操作,以避免阻塞 UI 线程(尤其在桌面和 Web 应用中至关重要)。
    • await dbContext.Blogs.ToListAsync();
  • 并发控制:处理多个用户同时修改同一数据时可能发生的冲突。
  • 存储过程:如何调用数据库中的存储过程。

学习资源与最佳实践

官方文档

视频教程

  • Phillip Japikse 的 Pluralsight 课程:非常经典和深入。
  • FreeCodeCamp 的 YouTube 视频:免费且内容质量高。
  • Bilibili:搜索 “.NET EF Core” 或 “Entity Framework Core”,有大量国内博主制作的优秀教程。

最佳实践

  1. 不要在 DbContext 中放置业务逻辑DbContext 应该只负责数据访问。
  2. 使用仓储模式:对于大型项目,可以使用仓储模式来封装数据访问逻辑,使代码更解耦。
  3. 谨慎使用 AsNoTracking():对于只读查询,使用 AsNoTracking() 可以显著提高性能,因为它会禁用 EF Core 的变更跟踪功能。
  4. 依赖注入:在现代 .NET 应用中,始终通过依赖注入来使用 DbContext,而不是在类中 new 一个实例。
  5. 保持模型简单:模型类应尽量保持“贫血”(Anemic),即只包含数据属性和行为,复杂的业务逻辑应放在服务层。

希望这份详细的教程能帮助你顺利入门 .NET EF Core!祝你学习愉快!