Entity Framework

技术栈
数据库
entity-frameworkef-coreormc#database毕设

概览

Entity Framework 技术栈概览

Entity Framework(EF)是微软官方的 .NET ORM 框架,将数据库表映射为 C# 对象,使开发者可以用 LINQ 操作数据库而无需写 SQL。Entity Framework Core(EF Core)是跨平台重构版。

解决什么问题

  • 数据库 CRUD 简化:用 C# 类表达数据模型,自动生成 SQL
  • Code First 迁移:先写 C# 模型,自动生成数据库表和迁移脚本
  • 变更追踪:上下文自动跟踪对象变更,SaveChanges() 一键持久化
  • 毕设后端:ASP.NET Core + EF Core 构建 REST API,快速出活

关键特性

  • LINQ 查询:强类型查询,编译期检查,避免 SQL 注入
  • Code First / DB First:支持从代码生成库、从库生成代码
  • Migration 迁移:版本化数据库架构变更,团队协作友好
  • 懒加载/急加载:灵活的关联数据加载策略
  • 多数据库支持:SQL Server / SQLite / PostgreSQL / MySQL / 内存数据库
  • 拦截器与日志:可记录生成的 SQL,方便调试

安装

Entity Framework Core 安装指南

1. 环境准备

要求 说明
操作系统 Windows / macOS / Linux 均可
.NET SDK .NET 6.0+ / .NET 8.0(推荐)
IDE Visual Studio 2022 / VS Code + C# 扩展 / JetBrains Rider
数据库(可选) SQL Server / SQLite / PostgreSQL / MySQL

2. 安装命令

通过 NuGet 安装

# 在项目目录下执行
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer   # SQL Server
# 或
dotnet add package Microsoft.EntityFrameworkCore.Sqlite         # SQLite(毕设推荐)
# 或
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL       # PostgreSQL

# 迁移工具(用于 CLI 创建迁移)
dotnet add package Microsoft.EntityFrameworkCore.Design

安装 EF Core CLI 工具(全局)

dotnet tool install --global dotnet-ef
# 验证
dotnet ef --version

快速创建带 EF Core 的项目

dotnet new webapi -n MyApi
cd MyApi
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design

3. 常见安装问题

问题 解决方案
dotnet ef 命令未找到 先安装全局工具:dotnet tool install --global dotnet-ef
Microsoft.EntityFrameworkCore.Design 缺失 迁移和逆向工程需要此包,务必安装
版本不兼容 EF Core 主版本号需匹配 .NET 版本(EF Core 8 → .NET 8)
SQLite 中文搜索不生效 SQLite 默认不区分大小写,精确搜索使用 StringComparison.Ordinal
迁移执行失败 检查连接字符串、数据库是否存在、用户权限

示例

Entity Framework Core 入门——学生数据库 CRUD

目标

使用 EF Core + SQLite 构建一个学生管理系统,演示 Code First、迁移、CRUD 全流程。

完整代码

1. 数据模型 Models/Student.cs

namespace StudentDb.Models;

public class Student
{
    public int Id { get; set; }           // 主键,自动递增
    public string Name { get; set; } = "";
    public int Age { get; set; }
    public double Score { get; set; }
    public DateTime EnrollDate { get; set; } = DateTime.Now;
}

public class Course
{
    public int Id { get; set; }
    public string Title { get; set; } = "";
    public int Credits { get; set; }
    public List<Student> Students { get; set; } = new();  // 多对多
}

2. 数据库上下文 Data/AppDbContext.cs

using Microsoft.EntityFrameworkCore;
using StudentDb.Models;

namespace StudentDb.Data;

public class AppDbContext : DbContext
{
    public DbSet<Student> Students => Set<Student>();
    public DbSet<Course> Courses => Set<Course>();

    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options) { }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 多对多关系配置
        modelBuilder.Entity<Course>()
            .HasMany(c => c.Students)
            .WithMany();
    }
}

3. 主程序 Program.cs

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using StudentDb.Data;
using StudentDb.Models;

var builder = Host.CreateApplicationBuilder(args);

// 注册 SQLite 数据库(文件存储,毕设推荐)
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlite("Data Source=students.db"));

var host = builder.Build();
var scope = host.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

// 自动创建数据库和表(开发环境用,生产用 Migration)
await db.Database.EnsureCreatedAsync();

Console.WriteLine("===== EF Core 学生管理系统 =====");

// ==== CREATE ====
Console.WriteLine("\n--- 添加学生 ---");
db.Students.AddRange(
    new Student { Name = "张三", Age = 22, Score = 88.5 },
    new Student { Name = "李四", Age = 21, Score = 92.0 },
    new Student { Name = "王五", Age = 23, Score = 76.5 }
);
await db.SaveChangesAsync();
Console.WriteLine("已添加 3 名学生");

// ==== READ ====
Console.WriteLine("\n--- 查询所有学生 ---");
var allStudents = await db.Students.ToListAsync();
foreach (var s in allStudents)
    Console.WriteLine($"  ID:{s.Id} {s.Name}, {s.Age}岁, {s.Score}分");

// LINQ 条件查询
Console.WriteLine("\n--- 成绩 >= 90 的学生 ---");
var topStudents = await db.Students
    .Where(s => s.Score >= 90)
    .OrderByDescending(s => s.Score)
    .ToListAsync();
foreach (var s in topStudents)
    Console.WriteLine($"  {s.Name}: {s.Score}分");

// ==== UPDATE ====
Console.WriteLine("\n--- 更新张三成绩 ---");
var zhangsan = await db.Students.FirstAsync(s => s.Name == "张三");
zhangsan.Score = 95.0;
await db.SaveChangesAsync();
Console.WriteLine($"张三新成绩: {zhangsan.Score}分");

// ==== DELETE ====
Console.WriteLine("\n--- 删除成绩低于 80 的学生 ---");
var lowStudents = await db.Students.Where(s => s.Score < 80).ToListAsync();
db.Students.RemoveRange(lowStudents);
var deleted = await db.SaveChangesAsync();
Console.WriteLine($"已删除 {deleted} 条记录");

// ==== 最终结果 ====
Console.WriteLine("\n--- 最终学生列表 ---");
foreach (var s in await db.Students.ToListAsync())
    Console.WriteLine($"  {s.Name}: {s.Score}分");

Console.WriteLine("\n按任意键退出...");
Console.ReadKey();

4. 项目文件 StudentDb.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.*" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.*" />
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.*" />
  </ItemGroup>
</Project>

运行步骤

dotnet new console -n StudentDb
cd StudentDb
# 添加 NuGet 包
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.Extensions.Hosting
# 替换代码后运行
dotnet run

预期输出

===== EF Core 学生管理系统 =====

--- 添加学生 ---
已添加 3 名学生

--- 查询所有学生 ---
  ID:1 张三, 22岁, 88.5分
  ID:2 李四, 21岁, 92分
  ID:3 王五, 23岁, 76.5分

--- 成绩 >= 90 的学生 ---
  李四: 92分

--- 更新张三成绩 ---
张三新成绩: 95分

--- 删除成绩低于 80 的学生 ---
已删除 1 条记录

--- 最终学生列表 ---
  张三: 95分
  李四: 92

关键点

  • DbContext 是数据库会话,管理实体操作
  • DbSet<T> 对应数据库表,可直接 LINQ 查询
  • SaveChangesAsync() 将内存变更批量写入数据库
  • EnsureCreated 适合毕设快速原型;正式项目用 Migration
  • SQLite 零配置,文件存储,毕设答辩演示最方便

教程

EF Core 毕设入门教程——数据库操作最佳实践

前言

Entity Framework Core 是 .NET 生态的 ORM 框架,让 C# 开发者用面向对象的方式操作数据库。毕设后端几乎必用它。


第一章:Code First vs DB First

Code First(毕设推荐)

// 1. 先写 C# 模型类
public class User { public int Id; public string Name; }

// 2. EF Core 自动生成数据库表
dotnet ef migrations add InitialCreate
dotnet ef database update

优点:版本可控、适合团队协作、代码即文档。

DB First(已有数据库)

dotnet ef dbcontext scaffold "Data Source=existing.db" Microsoft.EntityFrameworkCore.Sqlite

第二章:数据关系建模

一对一

public class User {
    public int Id { get; set; }
    public Profile? Profile { get; set; }
}
public class Profile {
    public int Id { get; set; }
    public int UserId { get; set; }
    public User User { get; set; } = null!;
}

一对多

public class Category {
    public int Id { get; set; }
    public List<Product> Products { get; set; } = new();
}
public class Product {
    public int Id { get; set; }
    public int CategoryId { get; set; }
    public Category Category { get; set; } = null!;
}

多对多(EF Core 5+ 自动处理)

public class Student {
    public int Id { get; set; }
    public List<Course> Courses { get; set; } = new();
}
public class Course {
    public int Id { get; set; }
    public List<Student> Students { get; set; } = new();
}

第三章:查询优化

急加载(避免 N+1 查询)

// 差:N+1 查询
var students = await db.Students.ToListAsync();  // 1次
foreach (var s in students)
    Console.WriteLine(s.Courses.Count);           // 每次循环1次!

// 好:一次查询
var students = await db.Students
    .Include(s => s.Courses)
    .ToListAsync();

投影查询(只取需要的字段)

var summaries = await db.Students
    .Select(s => new { s.Name, s.Score })
    .ToListAsync();
// 生成的 SQL:SELECT Name, Score FROM Students

分页

int page = 1, pageSize = 20;
var pageData = await db.Students
    .OrderBy(s => s.Id)
    .Skip((page - 1) * pageSize)
    .Take(pageSize)
    .ToListAsync();

第四章:事务与并发

事务

using var transaction = await db.Database.BeginTransactionAsync();
try {
    db.Orders.Add(order);
    db.Inventory.Remove(stock);
    await db.SaveChangesAsync();
    await transaction.CommitAsync();
} catch {
    await transaction.RollbackAsync();
}

乐观并发(防止数据覆盖)

public class Student {
    public int Id { get; set; }
    [ConcurrencyCheck]
    public byte[] Version { get; set; } = Array.Empty<byte>();
}

第五章:毕设最佳实践清单

  • ✅ 使用 SQLite 开发(零配置),答辩前可切换到 SQL Server/PostgreSQL
  • appsettings.json 配置连接字符串,不要硬编码
  • ✅ 开发环境开启日志看 SQL:options.LogTo(Console.WriteLine)
  • ✅ 及时 usingDispose DbContext
  • ✅ 生产环境用 Migration 而非 EnsureCreated

思考题

  1. FindAsync()FirstOrDefaultAsync() 的区别?
  2. 什么是 N+1 查询问题?如何用 Include 解决?
  3. DbContext 的生命周期应该怎么管理(Transient / Scoped / Singleton)?