c#软件开发学习笔记--EF框架、LINQ
EF框架学习总结
一、EF框架概述
1.1 概念定义
实体框架(EntityFramework) 是一种对象关系映射器(ORM),它使.NET开发人员能够通过.NET对象来操作数据库。它消除了开发人员通常需要编写的大多数数据访问代码的需求。
ORM(对象关系映射) 是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。即:把数据库中的表结构映射成语言中的对象。
1.2 EF框架优势
- 解放开发人员编写数据库操作逻辑,把关注点转移到业务逻辑
- 提高开发效率,开发快,节省成本
- 微软官方推广的重要ORM框架
1.3 EF版本说明
| 版本 | 特点 | 平台支持 |
|---|---|---|
| .NET Framework版本 | EF6.x及以下 | 仅支持Windows平台 |
| .NET Core版本 | EF Core | 跨平台支持 |
二、EF框架组成
2.1 EDM(实体数据模型)
EDM包含三个主要部分:
| 组成部分 | 说明 | 类比 |
|---|---|---|
| 概念模型(Conceptual Model) | 包含模型类和它们之间的关系,独立于数据库表设计 | 想法/设计思路 |
| 存储模型(Storage Model) | 数据库设计模型,包括表、视图、存储过程等及其关系 | 实际数据库设计 |
| 映射(Mapping) | 概念模型如何映射到存储模型的信息 | 转换规则 |
2.2 查询语言
- LINQ To Entity(L2E):查询实体对象的语言,返回概念模型中定义的实体
- Entity SQL:类似于L2E的查询语言,比L2E更加复杂
2.3 数据访问层
- Object Services(对象服务):访问数据库数据的主要入口点,负责数据实例化
- Entity Client Data Provider:将L2E或Entity Sql转换成数据库可识别的SQL语句
- ADO.Net Data Provider:使用标准ADO.net与数据库通信
三、EF三种使用方式
3.1 数据库优先(Database First)
适用场景:数据库已经设计好,基本不改动的情况下使用
核心特点:
- 先有数据库,再生成实体模型
- 通过EDMX文件可视化设计
- 自动生成实体类和DbContext
3.2 模型优先(Model First)
适用场景:先设计模型,再根据模型生成数据库实体对象
核心特点:
- 先设计EDMX模型图
- 根据模型自动生成数据库表结构
- 自动生成实体类和DbContext
3.3 代码优先(Code First)⭐
适用场景:推荐使用,先写代码,再生成数据库
核心特点:
- 纯代码方式定义实体模型
- 支持数据迁移(Migration)
- 灵活性最高
代码优先使用步骤:
# 1. 安装EF框架
Install-Package EntityFramework
# 2. 启用数据迁移(一次性)
Enable-Migrations
# 3. 添加迁移版本
Add-Migration 版本名称
# 4. 更新数据库
Update-Database
四、EF重要文件
| 文件类型 | 说明 | 示例 |
|---|---|---|
| .edmx | 实体数据模型文件,包含概念模型、存储模型和映射 | Model1.edmx |
| .tt | 模板文件,生成模型、上下文等使用的模板 | Model1.tt |
| .Context.cs | 实体上下文,数据库上下文类 | Model1.Context.cs |
| .cs(实体类) | 模型类文件,领域模型(Domain Model) | UserInfo.cs |
五、PM包管理器详解
5.1 概念说明
PM(Package Manager)包管理器:可以从NuGet上下载第三方模块包(类库、程序集),也可以上传自己开发的模块包。
NuGet:类似于手机上的"应用市场",提供平台供开发者上传、下载分享的模块包。
5.2 打开方式
VS2022 → 工具 → NuGet包管理器 → 程序包管理器控制台
注意:执行命令前需在包管理器顶部切换到当前项目。
5.3 常用命令
# 安装程序集
Install-Package 模块包名称
# 卸载程序集
Uninstall-Package 模块包名称
# 安装EF框架
Install-Package EntityFramework
六、数据迁移指令详解
6.1 初始化迁移(首次使用必执行)
Enable-Migrations
作用:
- 在项目自动生成
Migrations文件夹和配置类Configuration.cs - 只运行一次,后续不用重复执行
- 生成的配置文件控制自动迁移、种子数据等
6.2 生成迁移文件(记录实体改动)
Add-Migration 自定义迁移名称
# 示例
Add-Migration _20010
Add-Migration Add_Status2_Field
作用:
- 对比模型快照,生成继承
DbMigration的变更文件(包含Up/Down方法) - 执行后仅新增代码文件,不会修改数据库
- 修改实体后可重复执行同一条命令覆盖本次迁移
6.3 应用迁移(同步到数据库)
# 执行所有未运行的迁移,升级到最新版本
Update-Database
6.4 常用参数拓展
回滚到指定旧版本:
Update-Database -TargetMigration "_20010"
回滚到最初空库:
Update-Database -TargetMigration:$InitialDatabase
仅生成SQL脚本:
Update-Database -Script
强制执行:
Update-Database -Force
6.5 其他实用指令
获取当前所有迁移列表:
Get-Migrations
清除已执行迁移记录(慎用):
- 删除
Migrations文件夹全部文件 - 删除数据库表
__MigrationHistory - 重新执行
Add-Migration Init
6.6 指令完整使用流程
# 1. 首次项目开启迁移(仅1次)
Enable-Migrations
# 2. 修改实体类后,生成迁移记录文件
Add-Migration _20010
# 3. 将迁移同步到数据库
Update-Database
6.7 关键补充说明
- 所有命令在 NuGet包管理器控制台(PMC) 执行,不是CMD/PowerShell
- 执行前顶部「默认项目」要选中包含DbContext的项目
- Database First模式完全不需要以上任何迁移命令
Add-Migration只生成代码文件,必须搭配Update-Database才会改数据库- 回滚本质:执行对应迁移类的
Down()方法,撤销Up里的操作
七、LINQ概念与使用
7.1 LINQ概念
LINQ(Language Integrated Query) 是一种C#语言中的查询技术,它允许我们在代码中使用类似SQL的查询语句来操作各种数据源。这些数据源可以是集合、数组、数据库、XML文档等等。LINQ提供了一种统一的编程模型,使我们能够使用相同的方式来查询和操作不同类型的数据。
7.2 LINQ组成部分
| 组成部分 | 说明 | 是否必选 |
|---|---|---|
| 数据源(Data source) | 集合、数组、数据库、XML文档等 | 必选 |
| 查询变量(Query variable) | 用于存储查询结果的变量 | 必选 |
| 查询表达式(Query expression) | 类似SQL语句的查询表达式,用于描述查询的逻辑和条件 | 可选 |
| 查询操作符(Query operator) | 用于执行各种查询操作,如筛选、排序、分组、投影等 | 可选 |
| 查询结果(Query result) | 查询操作的返回结果 | 必选 |
注意:数据源、查询变量、查询结果是必选项;查询表达式、查询操作符是可选项。
7.3 LINQ分类
LINQ To Entities
LINQ to Entities是Entity Framework的一部分,取代LINQ to SQL作为在数据库上使用LINQ的标准机制。Entity Framework是行业领先的对象-关系映射(ORM)系统,可以和多种数据库一起使用,并支持各种灵活、复杂的数据模型。
LINQ To Objects
主要负责对象的查询,是指直接对任意IEnumerable集合使用LINQ查询,无需使用中间LINQ程序或API。LINQ To Object提供的是内存中集合数据的实体映射。
LINQ To XML
在System.Xml.Linq命名空间下实现对XML的操作。采用高效、易用、内存中的XML工具在宿主编程语言中提供XPath/XQuery功能等。
7.4 LINQ语法
LINQ有两种主要使用方式:查询表达式和扩展方法。
7.4.1 LINQ查询表达式
查询表达式使用类似SQL的语法,通过关键字组合实现数据查询。
int[] scores = new int[] { 98, 45, 77, 888, 81, 51 };
// from关键字:从哪里查
// s:查询变量,查询变量名和命名相同
// in:在哪个数据源中查询
// scores:数据源(数组、集合、SQL数据库、XML文档)
// where:筛选条件(==、!=、>、<、>=、<=,逻辑运算符&&、||、!)
// orderby:排序(默认ascending升序,descending降序)
// select:结果投影
IEnumerable<string> query = from s in scores
where s >= 50 && s <= 100
orderby s descending
select s + "分";
Console.WriteLine(query.ToList()[0]);
Console.WriteLine(query.ToList().Count);
foreach (string i in query)
{
Console.WriteLine(i);
}
字符串筛选示例:
string[] names = { "吴亦凡", "罗志祥", "李云迪" };
// 查询以"凡"结尾的字符串
// where n.EndsWith("凡")
// 查询包含"吴"的字符串
// where n.IndexOf("吴") != -1
// 查询以"罗"开头的字符串
IEnumerable<string> query1 = from n in names
where n.StartsWith("罗")
select n;
Console.WriteLine(query1.Count());
foreach (string i in query1)
{
Console.WriteLine(i);
}
多数据源查询示例:
Student[] students = {
new Student(){Id=1,Name="吴亦凡",Favorites=new string[]{ "写代码1","打游戏1"} },
new Student(){Id=2,Name="蔡徐坤",Favorites=new string[]{ "写代码2","打篮球2","打游戏2"} },
new Student(){Id=3,Name="罗志祥",Favorites=new string[]{ "写代码3","打篮球3","打游戏3"} },
};
// 如果有多个数据源,一般后面的数据源能使用前面数据源中的数据,反过来不行
// 只能有一个结果集
var query2 = from s in students // 第一个数据源,类型Student
where s.Name == "吴亦凡" // 对第一个数据源筛选
from f in s.Favorites // 第二个数据源,类型string[]
where f.Contains("打篮球") // 对第二个数据源筛选
select f; // 结果集
foreach (var i in query2)
{
Console.WriteLine(i);
}
7.4.2 LINQ扩展方法
扩展方法通过链式调用实现查询,使用Lambda表达式作为参数。
int[] ints = { 1, 2, 3, 6, 5, 7 };
// Where():筛选,和Find类似,相当于where子句
// IEnumerable<int> query1 = ints.Where((item) => { return item >= 2; });
// IEnumerable<int> query2 = ints.Where(v => v >= 2);
// OrderBy:升序,等同于orderby ascending
// OrderByDescending:降序,等同于orderby descending
IEnumerable<int> query2 = ints.Where(v => v >= 2)
.OrderByDescending(v => v)
.Select(v => v);
foreach (var i in query2)
{
Console.WriteLine(i);
}
7.5 LINQ+EF实战
在EF框架中,LINQ是查询数据库的主要方式:
BankSystemEntities context = new BankSystemEntities();
// 查询所有用户
var allUsers = context.UserInfo.ToList();
// 根据ID查询
var user = context.UserInfo.Find(id);
// LINQ查询表达式
var activeUsers = from u in context.UserInfo
where u.Status == 1
orderby u.CreateTime descending
select new
{
u.UserId,
u.Account,
u.CreateTime
};
// LINQ扩展方法
var recentUsers = context.UserInfo
.Where(u => u.CreateTime > DateTime.Now.AddDays(-7))
.OrderByDescending(u => u.CreateTime)
.Take(10);
7.6 常用LINQ操作符
| 操作符 | 说明 | 示例 |
|---|---|---|
Where | 筛选满足条件的元素 | .Where(u => u.Status == 1) |
OrderBy | 升序排序 | .OrderBy(u => u.CreateTime) |
OrderByDescending | 降序排序 | .OrderByDescending(u => u.CreateTime) |
Select | 投影,选择指定属性 | .Select(u => u.Account) |
First | 返回第一个元素 | .First(u => u.Status == 1) |
FirstOrDefault | 返回第一个元素或默认值 | .FirstOrDefault(u => u.UserId == id) |
Single | 返回唯一满足条件的元素 | .Single(u => u.UserId == id) |
SingleOrDefault | 返回唯一元素或默认值 | .SingleOrDefault(u => u.UserId == id) |
Count | 返回元素数量 | .Count() |
Any | 判断是否存在满足条件的元素 | .Any(u => u.Account == "admin") |
All | 判断是否所有元素都满足条件 | .All(u => u.Status == 1) |
Take | 取前N个元素 | .Take(10) |
Skip | 跳过前N个元素 | .Skip(10) |
Contains | 判断是否包含指定元素 | .Contains("admin") |
GroupBy | 分组 | .GroupBy(u => u.Status) |
Join | 连接两个数据源 | .Join(orders, u => u.UserId, o => o.UserId, ...) |
八、代码实战示例
8.1 DbContext上下文类
public partial class BankSystemEntities : DbContext
{
public BankSystemEntities()
: base("name=BankSystemEntities")
{
}
// DbSet属性,对应数据库表
public virtual DbSet<UserInfo> UserInfo { get; set; }
public virtual DbSet<AddressInfo> AddressInfo { get; set; }
public virtual DbSet<CustomerInfo> CustomerInfo { get; set; }
public virtual DbSet<StudentInfo> StudentInfo { get; set; }
}
8.2 实体类定义
public partial class UserInfo
{
public UserInfo()
{
this.AddressInfo = new HashSet<AddressInfo>();
}
// 标量属性(对应数据库字段)
public int UserId { get; set; }
public string Account { get; set; }
public string Password { get; set; }
public int CreateUserId { get; set; }
public System.DateTime CreateTime { get; set; }
public Nullable<int> LastUpdateUserId { get; set; }
public Nullable<System.DateTime> LastUpdateTime { get; set; }
public int Status { get; set; }
// 导航属性(对应表关系)
public virtual ICollection<AddressInfo> AddressInfo { get; set; }
}
8.3 增删改查操作
添加数据
BankSystemEntities context = new BankSystemEntities();
UserInfo u1 = new UserInfo()
{
Account = "123",
Password = "123",
CreateUserId = 1,
LastUpdateTime = DateTime.Now,
CreateTime = DateTime.Now,
Status = 1
};
context.UserInfo.Add(u1); // 添加到上下文(未同步到数据库)
int row = context.SaveChanges(); // 保存更改(同步到数据库)
删除数据
// 方式一:使用Remove方法
UserInfo u1 = context.UserInfo.Find(id);
context.UserInfo.Remove(u1);
// 方式二:设置实体状态
context.Entry(u1).State = System.Data.Entity.EntityState.Deleted;
context.SaveChanges();
更新数据
UserInfo u1 = context.UserInfo.Single(m => m.UserId == id);
// 修改属性
u1.Password = "888888";
u1.Account = "787777";
// 标记为修改状态
context.Entry(u1).State = System.Data.Entity.EntityState.Modified;
context.SaveChanges();
查询数据
// 查询所有
var list = context.UserInfo.ToList();
// 根据ID查询
var user = context.UserInfo.Find(id);
// LINQ查询
var users = from u in context.UserInfo
where u.Status == 1
orderby u.CreateTime descending
select u;
8.4 导航属性使用
导航属性:实体类中用来表示「表与表之间外键关联关系」的属性。
// 一对多关系:一个用户对应多个地址
UserInfo u1 = context.UserInfo.Find(1);
foreach (var a in u1.AddressInfo.ToList())
{
MessageBox.Show(a.City);
}
九、配置文件说明
9.1 App.config配置
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="entityFramework"
type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
requirePermission="false" />
</configSections>
<entityFramework>
<providers>
<provider invariantName="System.Data.SqlClient"
type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
<connectionStrings>
<add name="BankSystemEntities"
connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;
provider=System.Data.SqlClient;
provider connection string="data source=LAPTOP-804BMAF5\SQLEXPRESS;
initial catalog=BankSystem;
integrated security=True;
trustservercertificate=True;
MultipleActiveResultSets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
</connectionStrings>
</configuration>
十、学习资源推荐
| 资源类型 | 链接 |
|---|---|
| EF官方文档 | https://learn.microsoft.com/zh-cn/ef/ef6/ |
| EF三种使用方式对比 | https://www.cnblogs.com/wfy680/p/11969505.html |
| 代码优先教程 | https://download.csdn.net/blog/column/7504784/84750516 |
| DataType属性 | https://blog.csdn.net/ultramand/article/details/130039001 |
十一、重点总结
11.1 核心要点
- EF是ORM框架:将数据库表映射为.NET对象,简化数据访问
- 三种使用方式:Database First、Model First、Code First(推荐)
- 数据迁移:Code First模式下必须掌握的技能,实现数据库版本控制
- DbContext:数据库上下文,是EF操作数据库的核心入口
- 导航属性:实现表之间关联关系的访问,无需手写联表SQL
11.2 关键命令
# 安装EF
Install-Package EntityFramework
# 启用迁移
Enable-Migrations
# 添加迁移
Add-Migration 版本名
# 更新数据库
Update-Database
11.3 开发流程
设计实体类 → 编写DbContext → 启用迁移 → 添加迁移 → 更新数据库 → 业务开发
↑ |
└───────────────────────────────────────────────┘
模型变更后重复迁移流程
更多推荐

所有评论(0)