EFCore入门学习笔记
前言:EFCore 到底是个啥
先记住一句话:
EFCore 就是一个翻译官,专门帮你把 C# 代码翻译成数据库能听懂的 SQL 语句。
你现在写程序,数据都是存在内存里的,程序一关就没了。做上位机、做数据采集,数据是要长期保存的,比如:
- 设备台账(每台设备的编号、名称、型号)
- 采集到的历史数据(温度、压力、产量)
- 报警记录
这些数据都要存到数据库里。数据库只认一种语言,叫 SQL,长这样:
1 | SELECT * FROM Products WHERE Price > 20; |
这行 SQL 的意思是”把价格大于 20 的产品全查出来”。看着还行,但一旦查询变复杂,SQL 就很长、很难写、还容易拼错。
EFCore 干的事,就是让你不用写 SQL,直接用 C# 就能操作数据库。你写 C#,它在背后帮你翻译成 SQL 发给数据库。
打个比方:数据库是一台只会说”数据库黑话”的机器,EFCore 是站在你身边的翻译官。你说普通话(C#),它替你翻译(生成 SQL),再把结果拿回来给你。
用 EFCore 有三大好处:
- 不用手写 SQL 语句
- 用你熟悉的 C# 就能增删改查,写法跟操作 List 列表差不多
- 换数据库方便。你现在用 SQL Server,以后公司若换成 MySQL 或者其他数据库,代码基本不用改,只改一行配置就能切换
所以,学 EFCore 之前,先记住三句话:
- EFCore 是连接 C# 程序和数据库的一座桥
- 数据库里的一张表,在程序里对应一个”类”(叫模型类)
- 操作数据库,就像操作一个 List 列表,记住最后要”保存”
接下来我们一步一步做,每一步你都能亲手跑起来,看到结果。
第零步:确认电脑准备好了
学 EFCore 需要三样东西:
- 装了 .NET SDK(你电脑上已经有了,版本是 9.0.308)
- 一个能写代码的工具(Visual Studio、VS Code、Rider 都行)
- 装了 SQL Server(你本机已经装好了,而且用 Windows 身份验证登录,下面的连接字符串都按这个情况来写)
先验证一下第一样,打开命令行(Windows 里按 Win 键,输入 cmd 回车,或者用 VS Code 自带的终端),输入:
1 | dotnet --version |
如果显示出一串版本号(比如 9.0.308),就说明 .NET 环境没问题。
SQL Server 你已经装在本机了,这一关算过。后面第四步写连接字符串的时候,会用到”本机”和”Windows 身份验证”这两个信息,你跟着写就行。
第一步:新建一个项目
我们从一个空的控制台项目开始,这样最干净,没有多余的东西干扰。
先找一个放代码的文件夹。比如就放在 D 盘,打开命令行,输入:
1 | dotnet new console -n MyShop |
这行命令的意思是:新建一个控制台项目,项目名叫 MyShop(商店的意思,因为我们后面要存”产品”数据)。
命令执行完,会出现一个叫 MyShop 的文件夹。进入它:
1 | cd MyShop |
现在先跑一下,确认项目是好的:
1 | dotnet run |
如果控制台输出 Hello, World! 就说明项目建成功了。
补充一句:dotnet new console 这个命令,-n 后面的名字你可以自己起,不一定要叫 MyShop。后面所有代码都假设你用了 MyShop 这个名字,如果你改了名,代码里的名字也要跟着改。
第二步:安装 EFCore 的包
EFCore 不是 .NET 自带的,需要我们从网上下载安装。因为我们要连 SQL Server,所以要装专门针对 SQL Server 的包。在命令行输入:
1 | dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 9.0.13 |
回车后会看到它下载安装的过程,最后出现 “已成功还原” 之类的提示就算好了。
这里要特别提醒两点:
第一,包名里的 SqlServer 不能错。EFCore 对不同数据库有不同的包,连 SQL Server 就装 Microsoft.EntityFrameworkCore.SqlServer。装错了(比如装成 Sqlite 的),后面会连不上。
第二,命令最后多出来的 --version 9.0.13 一定要加上,不能省。原因是你电脑装的是 .NET 9,如果不写版本号,它会自动去装最新版(现在是 10.x),而 10.x 要求 .NET 10 才能用,会直接报错装不上。加上 --version 9.0.13,就是指定装能和 .NET 9 配对的版本,前面的 9 正好对应你的 .NET 版本号。
这一行命令装的是什么?就是 EFCore 操作 SQL Server 数据库的零件。注意:装了它,就自动包含了 EFCore 的核心,不用再单独装别的包。
装完之后,项目文件夹里有个 MyShop.csproj 文件,用记事本打开看一眼,里面会多出类似这样的一行:
1 | <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.13" /> |
这就说明包装好了。
第三步:认识模型类,程序里的”表”
数据库里有一张张的”表”,比如产品表、设备表。在 EFCore 里,一张表对应一个 C# 类,这个类就叫”模型类”。
类里的每个属性,就是表里的一列。
我们先做一张”产品表”,保存产品信息。在 MyShop 文件夹里,新建一个文件,名字叫 Product.cs(用 VS 的话,右键项目,添加,类,起名 Product;用 VS Code 的话,直接右键新建文件输入名字)。
在 Product.cs 里写:
1 | namespace MyShop; |
逐行解释一下:
| 代码 | 意思 |
|---|---|
| public class Product | 定义一个类,叫 Product,它将来对应一张”产品”表 |
| public int Id { get; set; } | 属性 Id,是每条数据的编号,EFCore 看到名字是 Id,会自动把它当成主键 |
| public string Name { get; set; } = “” | 属性 Name,存产品名称。= “” 是先给个空字符串,避免出现黄色警告 |
| public decimal Price { get; set; } | 属性 Price,存价格。decimal 是存钱专用的类型,不会出现小数点误差 |
| public int Stock { get; set; } | 属性 Stock,存库存数量 |
先记住这个对应关系:
一个类,对应一张表;类的一个属性,对应表里的一列。
现在这个类还只是普通的 C# 类,它自己还不会变出表来。要让 EFCore 认识它,还需要下一步的”数据库上下文”。
第四步:认识数据库上下文,也就是翻译官
光有模型类还不够,我们得有一个”翻译官”来牵线。这个翻译官,在 EFCore 里叫”数据库上下文”,英文是 DbContext。
再新建一个文件,叫 AppDbContext.cs,写:
1 | using Microsoft.EntityFrameworkCore; |
逐行解释:
| 代码 | 意思 |
|---|---|
| public class AppDbContext : DbContext | 定义类 AppDbContext,冒号后面表示”继承” DbContext。继承你可以先理解成”把它爹的本事全拿过来用” |
| public DbSet |
声明一张表,表名叫 Products,它里面放的是 Product 类型的数据。以后操作这张表,就用 Products 这个名字 |
| OnConfiguring | 一个固定的方法,在里面告诉 EFCore 连哪个数据库 |
| UseSqlServer(“…”) | 用 SQL Server,括号里那一长串叫”连接字符串”,下面专门讲 |
连接字符串到底写了啥
SQL Server 和”一个文件就是一个数据库”那种数据库不一样。SQL Server 是一个一直在后台运行的服务,你的数据都存在这个服务里。所以要连上它,得告诉它三件事:服务器在哪、进哪个数据库、用谁的身份登录。
我们写的这串:
1 | "Server=localhost;Database=MyShop;Trusted_Connection=True;TrustServerCertificate=True;" |
拆开看:
| 部分 | 意思 |
|---|---|
| Server=localhost | 连本机的 SQL Server。localhost 就是”这台电脑自己” |
| Database=MyShop | 进哪个数据库,这里叫 MyShop |
| Trusted_Connection=True | 用 Windows 身份验证登录,也就是你说的”电脑开机账号直接登录,不用输密码” |
| TrustServerCertificate=True | 信任本机服务器的证书。这句新手最容易漏,漏了会报证书错误 |
最后那句 TrustServerCertificate=True 要特别说一下:新版 .NET 默认要求加密连接,而本机 SQL Server 的证书是自己签的,系统不认,就会报错。加上这句,就是告诉程序”本机的证书我信了,别拦我”。
如果连不上,看这里
如果你运行时报错,说连不上服务器,多半是你装的是 SQL Server Express 版,它的实例名不是默认的。Express 版的实例名通常是 SQLEXPRESS,这时要把 Server=localhost 改成 Server=localhost\SQLEXPRESS。
注意,在 C# 代码里,反斜杠要写两个,写成:
1 | optionsBuilder.UseSqlServer("Server=localhost\\SQLEXPRESS;Database=MyShop;Trusted_Connection=True;TrustServerCertificate=True;"); |
因为单个反斜杠在 C# 字符串里是特殊含义,写两个才代表一个真正的反斜杠。
那怎么确认自己到底要不要改?打开 SQL Server Management Studio(简称 SSMS)的登录窗口,看”服务器名称”那一栏默认显示的是什么。如果显示的是”计算机名”(不带反斜杠),就用 Server=localhost;如果显示的是”计算机名\SQLEXPRESS”这种带反斜杠的,就把连接字符串改成带 \SQLEXPRESS 的写法。
到这里,两个关键的东西都齐了:
- Product:模型类,就是表的样子
- AppDbContext:翻译官,负责建表、操作数据
第五步:让 EFCore 自动建出数据库
现在我们还没建数据库,也没建表。EFCore 可以帮你自动把数据库和表都建好,不用你手动去建。
打开 Program.cs,把里面原来的 Hello World 代码全删掉,换成:
1 | using MyShop; |
解释一下这个代码:
| 代码 | 意思 |
|---|---|
| new AppDbContext() | 创建翻译官对象,起名 db |
| db.Database.EnsureCreated() | 让 EFCore 检查 SQL Server 里有没有 MyShop 这个数据库,没有就自动创建,并且把表也建好 |
| using (var db = …) | 用完之后自动释放资源,先照抄,不影响理解 |
现在运行:
1 | dotnet run |
如果输出”数据库建好了”,恭喜你,数据库创建成功了。
怎么确认它真的建好了?打开 SSMS,连接本机,在左边展开”数据库”,你应该能看到一个叫 MyShop 的数据库,展开它,里面有一张 Products 表。
如果你现在不想开 SSMS 也没关系,直接进入第六步,用插入和查询代码,同样能验证数据真的存进去了。
到这里,你已经完成了最难的部分:把模型类、翻译官、数据库全部连起来了。接下来就是增删改查,反而简单。
第六步:往数据库里加数据(增)
存数据,在程序里就是:新建一个 Product 对象,塞给翻译官,然后”保存”。
把 Program.cs 改成这样:
1 | using MyShop; |
运行:
1 | dotnet run |
输出”两条数据存进去了”就成功了。
这里有两个地方要重点记住:
第一,15.5m 这个 m 不能省。因为 Price 是 decimal 类型,而 C# 里直接写 15.5 默认是 double 类型,不匹配会报错。在小数后面加个 m,就是告诉 C#:这是 decimal。15.5m 读作”十五块五毛的 decimal”。
第二,SaveChanges() 千万不能忘。db.Products.Add(p1) 只是把 p1 记在”待保存清单”上,还没真正写进数据库。只有调用了 SaveChanges(),数据才真正落盘。忘了这行,程序跑完你会发现数据库里啥都没有。
把这三步连起来记:
Add(加入待保存清单)加 SaveChanges(真正写入)等于数据入库。
第七步:把数据读出来(查)
查数据就是把表里的数据读成 C# 对象,然后打印。
把 Program.cs 改成:
1 | using MyShop; |
运行:
1 | dotnet run |
你会看到上一节存进去的两条数据被打印出来了。
解释一下:db.Products.ToList() 的意思是”把产品表里的所有数据,读成一个 List 集合”。读出来之后,Products 里的每个元素就是一个 Product 对象,你就可以像操作普通 List 一样用 foreach 遍历它。
除了查全部,还有两种常用的查法。
按主键编号查一条,用 Find:
1 | using MyShop; |
按条件查,用 Where。这里就用到了你之前学过的 Lambda:
1 | using MyShop; |
p => p.Price > 20 读作:对每条数据 p,判断它的价格是否大于 20。Where 会把满足条件的都挑出来。你看,之前学的 Lambda 在这里直接派上用场了。
记住三种查法:
| 写法 | 作用 |
|---|---|
| db.Products.ToList() | 查全部 |
| db.Products.Find(编号) | 按主键编号查一条 |
| db.Products.Where(条件).ToList() | 按条件查一批 |
第八步:改数据
改数据的套路是:先查出来,改它的属性,再保存。
把 Program.cs 改成:
1 | using MyShop; |
运行:
1 | dotnet run |
输出”改好了,现在的价格是:18”就成功了。你可以用第七步的查询代码再跑一遍,验证一下价格真的变成了 18。
顺便说一句:运行这一步时,控制台可能会冒出几条黄色警告,内容大概是”可能出现空引用”。这是正常的,不影响结果,你先把程序跑通就行,不用管它。为什么会有这个警告,第十步的坑四会讲清楚。
改数据就这么简单,三步:查出来、改属性、SaveChanges。不用写任何 SQL 的 UPDATE 语句。
第九步:删数据
删数据的套路和改差不多:先查出来,再删,再保存。
把 Program.cs 改成:
1 | using MyShop; |
运行:
1 | dotnet run |
输出”删掉了”就成功。再用查询代码跑一遍,你会发现编号 2 的”扳手”已经不见了。
这一步运行也会和改数据一样,冒出几条黄色警告,不用管,原因见坑四。
到这里,增删改查四个动作你就全部学会了。总结成一张表:
| 动作 | 核心代码 | 说明 |
|---|---|---|
| 增 | db.Products.Add(对象); db.SaveChanges(); | 先加入清单,再保存 |
| 查 | db.Products.ToList() / Find(编号) / Where(条件) | 读成 C# 对象 |
| 改 | 查到对象,改属性,db.SaveChanges() | 改完要保存 |
| 删 | db.Products.Remove(对象); db.SaveChanges() | 删完要保存 |
看出来了吗?规律很简单:除了”查”不用保存,其余增、改、删,最后都要 SaveChanges()。
第十步:几个容易踩的坑
坑一:忘了 SaveChanges
这是新手最常犯的错。Add 了、改了、删了,结果数据库里没变化,十有八九是忘了 SaveChanges。
记住:Add、改属性、Remove 都只是改动内存里的对象,只有 SaveChanges() 才把改动真正写进数据库。
坑二:小数没加 m
1 | // 错误:15.5 默认是 double,赋给 decimal 会报错 |
坑三:改了模型类,旧数据库没跟着变
比如你后来给 Product 类加了一个属性,再跑程序会发现报错,说表里没这列。这是因为 SQL Server 里的 MyShop 数据库还是老结构,EnsureCreated 不会去改已经存在的库和表。
入门阶段最简单的解决办法:把旧的 MyShop 数据库删掉,再重新 dotnet run,让 EFCore 重新建一个。删除方法:打开 SSMS,连接本机,在左边找到 MyShop 数据库,右键删除;或者新建一个查询,执行下面这条 SQL:
1 | DROP DATABASE MyShop; |
(正式项目里,改数据库结构用的是”迁移”Migration,不用删库重建。那是进阶内容,等入门熟了再说。)
坑四:对空表用 Find 或 First 会报错
1 | var p = db.Products.Find(1); // 如果表里没有编号 1 的数据,p 会是 null |
如果表里没有对应数据,Find 返回的是 null(空),再往下用 p.Name 就会崩。所以用之前最好先判断一下:
1 | var p = db.Products.Find(1); |
其实第八步、第九步运行时看到的那些黄色警告,根源就是这里:Find 有可能返回 null,编译器在提醒你小心。因为我们是先存了数据再去查,肯定查得到,所以那些警告可以放心忽略。以后写正式程序,建议都像上面这样先判断 p 是不是 null 再用。
坑五:字符串属性有黄色警告
如果 Product 类里的 Name 写成 public string Name { get; set; },编译器可能给你一条黄色警告,说这个属性可能为空。不影响运行,但看着烦。加上 = "" 给它个默认空字符串就好了。
坑六:连不上服务器,多半是实例名不对
这是 SQL Server 特有的坑。运行时报错说连不上服务器,最常见的原因有两个:
第一个,实例名不对。如果是 Express 版,实例名通常是 SQLEXPRESS,连接字符串里要写成 Server=localhost\SQLEXPRESS(注意两个反斜杠),而不是 Server=localhost。怎么确认,回第四步的”如果连不上,看这里”。
第二个,忘了写 TrustServerCertificate=True。漏了这句,会报一个和证书有关的错。把它加上就好。
小结
把今天学的东西串起来回顾一遍:
- EFCore 是连接 C# 程序和数据库的翻译官
- 一个模型类(比如 Product)对应数据库里的一张表
- 数据库上下文 AppDbContext 是翻译官,负责建表、操作数据
- 连接字符串告诉 EFCore:服务器在哪、进哪个库、用谁登录
- EnsureCreated 帮你自动建库建表
- 增删改查的套路:查出来、改对象、最后 SaveChanges
用一句话记住整个 EFCore 的入门核心:
把表当成类,把数据当成对象,把操作当成 List,最后记得保存。
下一步可以学什么
入门这一关过了之后,有两条路可以往下走:
第一条路,学”迁移”Migration。正式项目里,数据库结构会不断改,不能每次都删库重建。迁移能帮你安全地升级数据库结构,是实际工作中一定会用到的。
第二条路,学账号密码登录和远程连接。现在你用的是本机的 Windows 身份验证。以后连公司的 SQL Server,往往要改成用账号密码登录,连接字符串里的 Server 也要写成公司服务器的地址。这个等你有具体服务器了再学也不迟。
建议先反复把今天这几步多跑几遍,把增删改查写熟了,再往这两条路走。
练习题
自己动手写一写,写出来才算真的会。每道题都建一个新项目,或者接着用 MyShop 改。
练习一
定义一个”设备”模型类 Device,属性有:Id(编号)、DeviceName(设备名)、Temperature(温度,double 类型)、IsOnline(是否在线,bool 类型)。建一个对应的 DbContext,用 EnsureCreated 建出数据库。
练习二
往设备表里存两条数据,比如”一号风机,温度 65,在线”和”二号水泵,温度 40,离线”。存进去之后,用查询把两条都打印出来。
练习三
把二号水泵改成在线状态,再改一下它的温度,然后保存。查一遍验证改动生效了。
练习四
删掉其中一台设备,再查一遍,确认它没了。
练习五
用 Where 和 Lambda,挑出所有温度大于 50 的设备,打印出来。(提示:和价格大于 20 的写法一样,把 Price 换成 Temperature 就行。)
建议:把每一步的代码都亲手敲一遍,不要复制粘贴,敲完跑一次看看输出。EFCore 这东西,看十遍不如自己敲一遍。等你把增删改查写顺手了,以后做数据看板、做设备台账,脑子里就有底了。