02020402 EF Core基础02-EF Core数据的增删改查
1. EF Core数据的增删改查(视频3-3)
- 创建.NET Core控制台项目,项目文档如下。
// Book.cs
using System;namespace EF CoreDemo
{public class Book{public long Id { get; set; } //主键public string Title { get; set; }//标题public DateTime PubTime { get; set; }//发布日期public double Price { get; set; }// 价格public string AuthorName { get; set; } // 新建属性(对应数据表中的行)}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// BookConfig.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;namespace EF CoreDemo
{class BookConfig : IEntityTypeConfiguration<Book> // 泛型接口,这个Book表示是与哪个实体类配置。{public void Configure(EntityTypeBuilder<Book> builder){builder.ToTable("T_Books"); // T_Books表与Book实体类相对应builder.Property(e => e.Title).HasMaxLength(50).IsRequired(); // @1builder.Property(e => e.AuthorName).HasMaxLength(20).IsRequired(); // @2}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// Person.cs
namespace EF CoreDemo
{public class Person{public long Id { get; set; }public long Name { get; set; }public int Age { get; set; } // 修改Age为int类型public string BirthPlace { get; set; } // 新增属性public double? Salary { get; set; } // 可为空的double类型}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// PersonConfig.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;namespace EF CoreDemo
{class PersonConfig : IEntityTypeConfiguration<Person>{public void Configure(EntityTypeBuilder<Person> builder){builder.ToTable("T_Persons");}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// Dog.cs
namespace EF CoreDemo
{public class Dog{public long Id { get; set; } //主键public string Name { get; set; }//标题}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// MyDbContext.cs
using Microsoft.EntityFrameworkCore;namespace EF CoreDemo
{class MyDbContext : DbContext{public DbSet<Book> Books { get; set; }public DbSet<Person> Persons { get; set; }public DbSet<Dog> Dogs { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){string connStr = "Server=.;Database=CoreDataDB;Trusted_Connection=True;MultipleActiveResultSets=true";optionsBuilder.UseSqlServer(connStr);}protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// Program.cs
using System;namespace EF CoreDemo
{class Program{static void Main(string[] args){}}
}
- 通过EF Core创建数据表
PM> add-migration addbirth
Build started...
Build succeeded.
To undo this action, use Remove-Migration.
PM> update-database
Build started...
Build succeeded.
Applying migration '20250918125952_Init'.
Done.
PM>
2. 插入数据
2.1 插入数据步骤
1、只要操作Books属性,就可以向数据库中增加数据,但是通过C#代码修改Books中的数据只是修改了内存中的数据。对Books做修改后,需要调用DbContext的异步方法SaveChangesAsync()把修改保存到数据库。也有同步的保存方法SaveChanges(),但是用EF Core都推荐用异步方法。
2、EF Core默认会跟踪(Track)实体类对象以及DbSet的改变。
3、演示数据插入。
2.2 插入数据示例
using System;
using System.Threading.Tasks;namespace EF CoreDemo
{class Program{static async Task Main(string[] args){// @1 插入数据using (MyDbContext ctx = new MyDbContext()) // ctx相当于逻辑上的数据库{Dog dog = new Dog();dog.Name = "Trump"; // 因为Dogs表的Id是自增的,因此只需要给Name赋值即可。ctx.Dogs.Add(dog); // 将dog对象添加到Dogs这个逻辑上的表里面。// ctx.SaveChanges(); // 保存修改,SaveChanges()是同步方法,相当于在程序包管理器控制台执行update-databaseawait ctx.SaveChangesAsync(); // 保存修改,异步方法。}Console.WriteLine("数据插入成功");Console.ReadLine();}}
}控制台输出:
数据插入成功
- 通过SSMS查看数据

3. 查询数据
3.1 给数据库插入数据用于查询
using System;
using System.Threading.Tasks;namespace EF CoreDemo
{class Program{static async Task Main(string[] args){// @1 插入数据using (MyDbContext ctx = new MyDbContext()) // ctx相当于逻辑上的数据库{Book b1 = new Book { AuthorName = "杨中科", Title = "零基础趣学C语言", Price = 59.8, PubTime = new DateTime(2019, 3, 1) };Book b2 = new Book { AuthorName = "Robert Sedgewick", Title = "算法第4版", Price = 99, PubTime = new DateTime(2012, 10, 1) };Book b3 = new Book { AuthorName = "吴军", Title = "数学之美", Price = 69, PubTime = new DateTime(2020, 5, 1) };Book b4 = new Book { AuthorName = "杨中科", Title = "程序员的SQL经典", Price = 52, PubTime = new DateTime(2008, 9, 1) };Book b5 = new Book { AuthorName = "吴军", Title = "文明之光", Price = 246, PubTime = new DateTime(2017, 3, 1) };ctx.Books.Add(b1);ctx.Books.Add(b2);ctx.Books.Add(b3);ctx.Books.Add(b4);ctx.Books.Add(b5);await ctx.SaveChangesAsync();}Console.WriteLine("数据插入成功");Console.ReadLine();}}
}控制台输出:
数据插入成功
- 数据库中的数据

3.2 查询数据
1、DbSet实现了IEnumerable<T>接口,因此可以对DbSet实施Linq操作来进行数据查询。EF Core会把Linq操作转换为SQL语句。面向对象,而不是面向数据库(SQL)。
2、ctx.Books.Where(b => b.Price > 80)
Book b1 = ctx.Books.Single(b => b.Title== "零基础趣学C语言");
Book b2 = ctx.Books.FirstOrDefault(b=>b.Id==9);
3、可以使用OrderBy操作进行数据的排序
IEnumerable<Book> books = ctx.Books.OrderByDescending(b => b.Price);
3.3 查询数据示例01
// Program.cs
using System;
using System.Threading.Tasks;
using System.Linq;namespace EF CoreDemo
{class Program{static async Task Main(string[] args){// @1 插入数据using (MyDbContext ctx = new MyDbContext()){IQueryable<Book> book01 = ctx.Books.Where(b => b.Price > 80); // @1foreach (Book book in book01){Console.WriteLine(book.Title);}Console.WriteLine("********************");Book book02 = ctx.Books.Single(b => b.Title == "零基础趣学C语言"); // @2Console.WriteLine(book02);Console.WriteLine(book02.AuthorName);Console.WriteLine("********************");IQueryable<Book> book03 = ctx.Books.OrderBy(b => b.Price); // 根据价格排序foreach(Book book in book03){Console.WriteLine(book.Title + "的价格为:" + book.Price);}Console.WriteLine("********************");IQueryable<Book> book04 = ctx.Books.OrderBy(b => b.Price).Where(b => b.Price > 80); // 根据价格排序,并且只取价格高于80的。foreach (Book book in book04){Console.WriteLine(book.Title + "的价格为:" + book.Price);}}// await ctx.SaveChangesAsync();Console.ReadLine();}}
}控制台输出:
算法第4版
文明之光
********************
EF CoreDemo.Book
杨中科
********************
程序员的SQL经典的价格为:52
零基础趣学C语言的价格为:59.8
数学之美的价格为:69
算法第4版的价格为:99
文明之光的价格为:246
********************
算法第4版的价格为:99
文明之光的价格为:246说明:
1. 在@1处,Where返回值类型为IQueryable类型。
1.1 IQueryable又继承自IEnumerable,IQueryable是EF Core里面的类型。
1.2 之前讲的LINQ有Where返回的是IEnumerable。
2. 在@2处,确认只有一本书,返回值就不是一个集合了。此时就不写IQueryable,而是直接返回Book类型。
3.4 查询数据示例02
1、GroupBy也可以
var groups = ctx.Books.GroupBy(b => b.AuthorName).Select(g => new { AuthorName = g.Key, BooksCount = g.Count(), MaxPrice = g.Max(b => b.Price) });
foreach(var g in groups)
{Console.WriteLine($"作者名:{g.AuthorName},著作数量:{g.BooksCount},最贵的价格:{g.MaxPrice}");
}
2、大部分Linq操作都能作用于EF Core。
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// Program.cs
using System;
using System.Threading.Tasks;
using System.Linq;namespace EFCoreDemo
{class Program{static async Task Main(string[] args){// @1 插入数据using (MyDbContext ctx = new MyDbContext()){var item = ctx.Books.GroupBy(b => b.AuthorName).Select(g => new { Name=g.Key, BooksCount=g.Count(), MaxPrice = g.Max(b => b.Price)});foreach (var bk in item){Console.WriteLine($"{bk.Name}, {bk.BooksCount}, {bk.MaxPrice}");}}Console.ReadLine();}}
}控制台输出:
Robert Sedgewick, 1, 99
吴军, 2, 246
杨中科, 2, 59.8
4. 修改数据
- 要对数据进行修改,首先需要把要修改的数据查询出来,然后再对查询出来的对象进行修改,然后再执行SaveChangesAsync()保存修改。
// 伪代码
var b = ctx.Books.Single(b=>b.Title== "数学之美");
b.AuthorName = "Jun Wu";
await ctx.SaveChangesAsync();
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 修改前数据库中信息
Id Title PubTime Price AuthorName
1 零基础趣学C语言 2019-03-01 00:00:00.0000000 59.8 杨中科
2 算法第4版 2012-10-01 00:00:00.0000000 99 Robert Sedgewick
3 数学之美 2020-05-01 00:00:00.0000000 69 吴军
4 程序员的SQL经典 2008-09-01 00:00:00.0000000 52 杨中科
5 文明之光 2017-03-01 00:00:00.0000000 246 吴军
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 修改数据:Program.cs
using System;
using System.Threading.Tasks;
using System.Linq;namespace EFCoreDemo
{class Program{static async Task Main(string[] args){// @1 插入数据using (MyDbContext ctx = new MyDbContext()){var b = ctx.Books.Single(b => b.Title == "数学之美"); // 找到对象,明确只有一个用Singleb.AuthorName = "Junwu"; // 修改对象属性await ctx.SaveChangesAsync(); // 更新数据库}Console.WriteLine("修改数据成功!");Console.ReadLine();}}
}控制台输出:
修改数据成功!
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 修改后数据库中信息
Id Title PubTime Price AuthorName
1 零基础趣学C语言 2019-03-01 00:00:00.0000000 59.8 杨中科
2 算法第4版 2012-10-01 00:00:00.0000000 99 Robert Sedgewick
3 数学之美 2020-05-01 00:00:00.0000000 69 Junwu // 这里已经将吴军修改为Junwu
4 程序员的SQL经典 2008-09-01 00:00:00.0000000 52 杨中科
5 文明之光 2017-03-01 00:00:00.0000000 246 吴军
5. 删除数据
- 删除也是先把要修改的数据查询出来,然后再调用DbSet或者DbContext的Remove方法把对象删除,然后再执行SaveChangesAsync()保存修改。
// 伪代码
var b = ctx.Books.Single(b => b.Title == "数学之美");
ctx.Remove(b);//也可以写成ctx.Books.Remove(b);
await ctx.SaveChangesAsync();
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 删除前数据库中信息
Id Title PubTime Price AuthorName
1 零基础趣学C语言 2019-03-01 00:00:00.0000000 59.8 杨中科
2 算法第4版 2012-10-01 00:00:00.0000000 99 Robert Sedgewick
3 数学之美 2020-05-01 00:00:00.0000000 69 Junwu
4 程序员的SQL经典 2008-09-01 00:00:00.0000000 52 杨中科
5 文明之光 2017-03-01 00:00:00.0000000 246 吴军
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 删除数据:Program.cs
using System;
using System.Threading.Tasks;
using System.Linq;namespace EFCoreDemo
{class Program{static async Task Main(string[] args){// @1 插入数据using (MyDbContext ctx = new MyDbContext()){var b = ctx.Books.Single(b => b.Id == 2); // 找到对象,明确只有一个用Singlectx.Books.Remove(b); // 删除对象await ctx.SaveChangesAsync(); // 更新数据库}Console.WriteLine("删除数据成功!");Console.ReadLine();}}
}控制台输出:
删除数据成功!
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 修改后数据库中信息
Id Title PubTime Price AuthorName
1 零基础趣学C语言 2019-03-01 00:00:00.0000000 59.8 杨中科
3 数学之美 2020-05-01 00:00:00.0000000 69 Junwu
4 程序员的SQL经典 2008-09-01 00:00:00.0000000 52 杨中科
5 文明之光 2017-03-01 00:00:00.0000000 246 吴军说明:Id为2的数据项已经删除。
6. 批量修改数据
- 价格大于60的所有书涨1块钱
// 数据库已有信息
Id Title PubTime Price AuthorName
1 零基础趣学C语言 2019-03-01 00:00:00.0000000 59.8 杨中科
3 数学之美 2020-05-01 00:00:00.0000000 69 Junwu
4 程序员的SQL经典 2008-09-01 00:00:00.0000000 52 杨中科
5 文明之光 2017-03-01 00:00:00.0000000 246 吴军
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 给价格大于60的所有书涨1块钱:Program.cs
using System;
using System.Threading.Tasks;
using System.Linq;namespace EFCoreDemo
{class Program{static async Task Main(string[] args){// @1 插入数据using (MyDbContext ctx = new MyDbContext()){var books = ctx.Books.Where(b => b.Price > 60); foreach(var bk in books){bk.Price = bk.Price + 1;}await ctx.SaveChangesAsync(); // 更新数据库}Console.WriteLine("价格大于60的书涨1块钱成功!");Console.ReadLine();}}
}控制台输出:
价格大于60的书涨1块钱成功!
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 更新后的数据库信息
Id Title PubTime Price AuthorName
1 零基础趣学C语言 2019-03-01 00:00:00.0000000 59.8 杨中科
3 数学之美 2020-05-01 00:00:00.0000000 70 Junwu
4 程序员的SQL经典 2008-09-01 00:00:00.0000000 52 杨中科
5 文明之光 2017-03-01 00:00:00.0000000 247 吴军
7. 跟踪数据库
7.1 跟踪数据库的步骤
- SSMS → 工具 → SQL Server Profiler → 连接到服务器面板 → 连接 → 跟踪属性(默认接口) → 运行 → 执行.NET Core控制台程序 → 可以看到如下执行SQL语句的代码。
![]() 查询的SQL语句 | ![]() 更新的SQL语句 |
- 从上述两张图片可以得出如下结论
- EF Core首先从数据库拿到数据,然后每个数据都需要update更新一次,有5条就执行5次。如果有1万条,就会执行1万条update。
- 如果直接执行SQL语句,只需要一个update即可。
- 对于数量比较大的,或者需要批量的更新或者删除,相对于直接执行SQL语句来说,EF Core的性能相对比较低。
8. EF Core关于批量处理数据的性能损失
8.1 EF Core处理批量数据性能损失的原因
1、目前批量修改、删除多条数据的方法。
局限性:性能低:查出来,再一条条Update、Delete,而不能执行Update ... where;delete ... where;
2、官方目前还没有支持高效的批量Update、Delete,有在后续版本中增加,但是目前只是前期意见征询阶段。
- EF Core在目前为止(讲课时间为:2023.07.21日)还没有高效的,批量的update和delete方法。这样做的目的,微软官方给出了如下理由。
- 为了保证内存中对象状态的一致性,EF Core在设计之初就没有考虑到批量的update和delete。
- 注:课程弹幕上提示.NET Core 7.0有了ExecuteUpdate和ExecuteDelete,用来批量的update和delete。
8.2 老师自己开发的一个批量、修改的开源项目
我实现了一个开源的高效批量修改、删除的开源项目。
Zack.EFCore.Batch https://github.com/yangzhongke/Zack.EFCore.Batch
- 用法:这个包可能存在数据库中数据删除了,但是还能查出来。这是因为数据缓存的问题,这个问题受限于EF Core,关于这个包了解即可。
// 批量删除
await ctx.DeleteRangeAsync<Book>(b => b.Price > n || b.AuthorName == "zack yang"); // 批量删除
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 批量修改
await ctx.BatchUpdate<Book>() .Set(b => b.Price, b => b.Price/3).Set(b => b.Title, b => s).Set(b => b.PubTime, b=> DateTime.Now).Where(b => b.Id > n || b.AuthorName.StartsWith("Zack")).ExecuteAsync();
结尾
书籍:ASP.NET Core技术内幕与项目实战
视频:https://www.bilibili.com/video/BV1pK41137He
著:杨中科
ISBN:978-7-115-58657-5
版次:第1版
发行:人民邮电出版社
※敬请购买正版书籍,侵删请联系85863947@qq.com※
※本文章为看书或查阅资料而总结的笔记,仅供参考,如有错误请留言指正,谢谢!※