文章目录
- 一、概述
- 二、使用NuGet安装
-
- [2.1 新建一个WPF项目](#2.1 新建一个WPF项目)
- [2.2 安装Entity Framework Core包](#2.2 安装Entity Framework Core包)
- 三、基本使用
-
- [3.1 创建数据库](#3.1 创建数据库)
-
- [3.1.1 创建两个模型类(对应数据库的两个表)](#3.1.1 创建两个模型类(对应数据库的两个表))
- [3.1.2 创建上下文对象](#3.1.2 创建上下文对象)
- [3.1.3 工程中主页面设计](#3.1.3 工程中主页面设计)
- [3.1.4 创建数据库](#3.1.4 创建数据库)
- [3.1.4 运行程序,创建数据库](#3.1.4 运行程序,创建数据库)
- [3.2 增删改查](#3.2 增删改查)
-
- [3.2.1 调整界面](#3.2.1 调整界面)
- [3.2.2 增加绑定数据源并绑定到界面中](#3.2.2 增加绑定数据源并绑定到界面中)
- [3.2.3 查询表数据操作](#3.2.3 查询表数据操作)
- [3.2.4 查询单条数据](#3.2.4 查询单条数据)
- [3.2.5 增加数据](#3.2.5 增加数据)
- [3.2.6 修改数据](#3.2.6 修改数据)
- [3.2.7 删除数据](#3.2.7 删除数据)
- [3.3 数据库迁移](#3.3 数据库迁移)
-
- [3.3.1 修改具体的模型实体类](#3.3.1 修改具体的模型实体类)
- [3.3.2 创建迁移 (Add-Migration)](#3.3.2 创建迁移 (Add-Migration))
- [3.3.3 迁移数据库](#3.3.3 迁移数据库)
- [3.3.4 删除迁移](#3.3.4 删除迁移)
悟剑声
诗号: 天外情风吹云立,红尘飞雨悟剑声。
悟剑声,霹雳布袋戏中的虚拟角色,伴随着雷雨交加而现的神秘少年剑客,身法奇快、剑法殊异,背着一口具有意识之剑,招式夹带紫电云光。其诗号为"天外情风吹云立,红尘飞雨悟剑声"。
其谜样的身份和武功属性,格外引人联想。泣龙怨内中藏有文诏,是东瀛军的象征。
一、概述
Entity Framework (EF) Core 是轻量化、可扩展、开源和跨平台版的常用 Entity Framework 数据访问技术。
EF Core 可用作对象关系映射程序 (O/RM),其功能包括以下几点:
- 使 .NET 开发人员能够使用 .NET 对象处理数据库。
- 无需再像通常那样编写大部分数据访问代码。
- EF Core 支持多个数据库引擎。
二、使用NuGet安装
2.1 新建一个WPF项目
直接用visual studio创建wpf项目。
框架:.net 10.0
2.2 安装Entity Framework Core包
需要安装的包如下:
- Entity FrameworkCore.xxx(具体数据的包)
- Microsoft.EntityFrameworkCore.Tools(工具包)
- Microsoft.EntityFrameworkCore.Proxies(延迟加载相关)
注: 各个包安装的版本需要保持一致,不然会出现冲突。
打开NuGet包管理器,搜索"Entity FrameworkCore.Mysql",这个按照自己使用的数据库,加尾缀直接搜,也可以搜索"Entity FrameworkCore"然后再找自己需要的数据库对应的包,mysql的包主要有两个:

对比如下:
| 特性 | Pomelo.EntityFrameworkCore.MySql | MySql.EntityFrameworkCore |
|---|---|---|
| 开发方/维护方 | Pomelo 基金会 主导的社区开源项目 | Oracle 公司官方推出的连接器包 |
| 核心优势 | 社区活跃,更新快 ,紧跟 EF Core 最新版本,采用高性能的 MySqlConnector 库,被广泛验证,稳定可靠 |
官方背景 ,在部分场景下可能是唯一合规选择(例如部分大型企业的 Oracle 生态内部项目) |
| 版本更新状态 | 非常积极,已发布支持 .NET 9 / EF Core 9 的正式版 | 虽然也在更新,但历史上更新节奏相对较慢,曾落后于 EF Core 版本,目前有支持 EF Core 9 的预览/测试版 |
| 一个典型的细节 | 版本号规则清晰,如 8.0.3 明确对应 EF Core 8 |
版本号可能包含 MySQL 版本信息,如 6.0.30+MySQL9.0.0,需要仔细核对对应关系 |
建议
-
大多数情况,选择 Pomelo :无论是你正在开发的 WPF 自动化检测项目,还是其他 Web 应用、微服务,
Pomelo.EntityFrameworkCore.MySql都是社区公认的首选。它更快的迭代速度和更活跃的社区支持,能让你更方便地使用 EF Core 的新特性,遇到问题也更容易找到解决方案。微软官方文档也将它列为 MySQL 的主要提供程序之一。 -
少数情况,选择官方包 :只有在项目有硬性要求必须使用 Oracle 官方组件 时(如特定的合规需求),才需要考虑
MySql.EntityFrameworkCore。同时需要注意,选择它意味着可能需要忍受较慢的更新节奏和潜在的兼容性问题。
我这边安装Pomelo的包,直接装最新版9.0.0。
安装时会自动安装相关的依赖库。

三、基本使用
3.1 创建数据库
3.1.1 创建两个模型类(对应数据库的两个表)
bash
/// <summary>
/// 类别
/// </summary>
public class Category
{
/// <summary>
/// 类别Id
/// </summary>
public int CategoryId { get; set; }
/// <summary>
/// 类别名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 类别下的产品集合
/// </summary>
public virtual ICollection<Product>
Products
{ get; private set; } =
new ObservableCollection<Product>();
}
bash
public class Product
{
/// <summary>
/// 产品Id
/// </summary>
public int ProductId { get; set; }
/// <summary>
/// 产品名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 类别Id
/// </summary>
public int CategoryId { get; set; }
/// <summary>
/// 类别
/// </summary>
public virtual Category Category { get; set; }
}
3.1.2 创建上下文对象
上下文对象模型映射是通过DBSet来映射的,相当于一个数据集。
理解上,可以理解成ADO的DataSet,比较类似。
通过重写OnConfiguring函数,配置数据库连接字符串,这边连接本地测试的mysql数据库。
bash
/// <summary>
/// 产品数据库上下文对象
/// </summary>
public class ProductContext : DbContext
{
/// <summary>
/// 产品集合
/// </summary>
public DbSet<Product> Products { get; set; }
/// <summary>
/// 类别集合
/// </summary>
public DbSet<Category> Categories { get; set; }
/// <summary>
/// 配置数据库上下文对象
/// </summary>
/// <param name="optionsBuilder"></param>
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionString = "server=localhost;user=root;password=root;database=test";
var serverVersion = new MySqlServerVersion(new Version(8, 0, 29));
optionsBuilder.UseMySql(connectionString, serverVersion);
optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);
optionsBuilder.EnableSensitiveDataLogging();
optionsBuilder.EnableDetailedErrors();
optionsBuilder.UseLazyLoadingProxies();
}
}
3.1.3 工程中主页面设计
我这边添加了2个按钮,一个创建数据,一个查询的按钮。
下方是一个RichTextBox,用来展示日志

后台代码:
封装了一个简单的日志打印函数,正确的黑色,失败红色。
bash
private void LogWrite(string msg)
{
Paragraph paragraph = (Paragraph)rtxt_Log.Document.Blocks.FirstBlock;
Run run = new Run(string.Format("{0} : {1}", DateTime.Now.ToString("HH:mm:ss"), msg))
{
Foreground = msg.Contains("失败") ? new SolidColorBrush(Colors.Red) : new SolidColorBrush(Colors.Black)
};
paragraph.Inlines.Add(run);
paragraph.Inlines.Add(new LineBreak());
}
创建数据库按钮事件,直接通过命令创建数据库:
3.1.4 创建数据库
直接使用EnsureCreated方法创建数据库
注: 测试时可用,官方推荐生产环境需要时候在程序外进行迁移升级,比较安全。
使用此方法会与迁移功能冲突,应为直接创建时,不会在数据库生成迁移记录表,等后续使用迁移功能时会报错,需要手动处理迁移数据记录才能正常迁移。
bash
/// <summary>
/// 创建数据库按钮点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button_Click(object sender, RoutedEventArgs e)
{
// 创建数据库
if (ProductDB.Database.EnsureCreated())
{
LogWrite("创建数据库成功!");
}
else
{
LogWrite("创建数据库失败!");
}
}
使用MigrateAsync来创建数据库
这是使用迁移功能来创建数据库,迁移时如果不存在数据库会自动创建,也会在数据库创建迁移记录表用作标识,后面可以正常完成数据库迁移功能,不用手动处理数据。
bash
/// <summary>
/// 创建数据库
/// </summary>
private async void CreateDatabase()
{
// 迁移数据库
await ProductDB.Database.MigrateAsync();
// 读取待执行的迁移记录,没有要继续迁移的记录标识迁移成功。
if (!(await ProductDB.Database.GetPendingMigrationsAsync()).Any())
{
LogWrite("创建数据库成功!");
}
else
{
LogWrite("创建数据库失败!");
}
}
用迁移方法创建,会自动创建迁移记录表,并添加一条初始化的迁移记录。

3.1.4 运行程序,创建数据库
启动程序,点击创建数据库按钮:

显示创建成功,查看下后台数据库。

数据库后台也正常创建。
注: 数据库已存在的情况,再次新增就会返回失败,不会修改到数据的信息。

3.2 增删改查
3.2.1 调整界面
增加两个DataGrid显示数据,增加增删改查的基本和保存按钮。

3.2.2 增加绑定数据源并绑定到界面中
创建资源
xml
<Window.Resources>
<CollectionViewSource x:Key="categoryViewSource"/>
<CollectionViewSource x:Key="categoryProductsViewSource"
Source="{Binding Products, Source={StaticResource categoryViewSource}}"/>
</Window.Resources>
设置分类表格式并绑定数据
xml
<DataGrid x:Name="grid_Categories" Grid.Column="0" AutoGenerateColumns="False"
ItemsSource="{Binding Source={StaticResource categoryViewSource}}"
RowDetailsVisibilityMode="VisibleWhenSelected">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding CategoryId}"
Header="Category Id" MinWidth="80" Width="SizeToHeader"
IsReadOnly="True" Visibility="Visible"/>
<DataGridTextColumn Binding="{Binding Name}" Header="Name"
Width="*" MinWidth="80"/>
</DataGrid.Columns>
</DataGrid>
设置产品表格式并绑定数据
xml
<DataGrid x:Name="grid_Products" Grid.Column="1" AutoGenerateColumns="False"
EnableRowVirtualization="True"
ItemsSource="{Binding Source={StaticResource categoryProductsViewSource}}"
RowDetailsVisibilityMode="VisibleWhenSelected">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding CategoryId}"
Header="Category Id" Width="SizeToHeader"
IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding ProductId}" Header="Product Id"
Width="SizeToHeader" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="*"/>
</DataGrid.Columns>
</DataGrid>
3.2.3 查询表数据操作
查询数据按钮增加查询事件处理函数。
csharp
if (categoryViewSource.Source == null)
{
grid_Categories.Items.Clear();
grid_Products.Items.Clear();
}
// 加载数据
ProductDB.Categories.Load();
LogWrite("分类表数据加载完毕!");
// 绑定集合数据
categoryViewSource.Source = null;
categoryViewSource.Source = ProductDB.Categories.Local.ToObservableCollection();
RefreshItem();
if (categoryViewSource.Source == null)
{
LogWrite("分类表集合为空!");
}
else
{
LogWrite("分类表集合加载成功!");
}
3.2.4 查询单条数据
在3.1的基础上,添加一些数据
bash
GetInfo();
if (rbtn_Category.IsChecked == true)
{
if (ID != null)
{
// 异步查询方法
category = ProductDB.Categories.Find(ID);
if (category == null)
{
LogWrite("未找到该分类!");
}
else
{
txt_Name.Text = category.Name;
LogWrite($"查询到分类:ID={category.CategoryId}, Name={category.Name}");
}
}
else
{
// 异步查询方法
category = ProductDB.Categories.FirstOrDefault(x => x.Name.Equals(Name));
if (category == null)
{
LogWrite("未找到该分类!");
}
else
{
txt_ID.Text = category.CategoryId.ToString();
LogWrite($"查询到分类:ID={category.CategoryId}, Name={category.Name}");
}
}
}
else
{
if (ID != null)
{
// 异步查询方法
product = ProductDB.Products.Find(ID);
if (product == null)
{
LogWrite("未找到该产品!");
}
else
{
txt_Name.Text = product.Name;
LogWrite($"查询到产品:ID={product.ProductId}, Name={product.Name}");
}
}
else
{
// 异步查询方法
product = ProductDB.Products.FirstOrDefault(x => x.Name.Equals(Name));
if (product == null)
{
LogWrite("未找到该产品!");
}
else
{
txt_ID.Text = product.ProductId.ToString();
LogWrite($"查询到产品:ID={product.ProductId}, Name={product.Name}");
}
}
}
RefreshItem();
3.2.5 增加数据
bash
private async void Add()
{
GetInfo();
ID = null; // 添加数据时,ID应为空
if (rbtn_Category.IsChecked == true)
{
category = new Category()
{
Name = Name,
};
// 异步查询方法
ProductDB.Add(category);
// 这边使用异步方法,需要搭配await关键字,添加数据后,会自动更新ID。
await ProductDB.SaveChangesAsync();
if (category.CategoryId == 0)
{
LogWrite("添加分类失败!");
}
else
{
LogWrite($"添加分类成功:ID={category.CategoryId}, Name={category.Name}");
}
}
else
{
product = new Product()
{
Name = Name,
CategoryId = category?.CategoryId ?? 0,
};
// 异步查询方法
ProductDB.Add(product);
await ProductDB.SaveChangesAsync();
if (product.ProductId == 0)
{
LogWrite("添加产品失败!");
}
else
{
LogWrite($"添加产品成功:CategoryId={product.CategoryId},ID={product.ProductId}, Name={product.Name}");
}
}
RefreshItem();
}
3.2.6 修改数据
bash
GetInfo();
if (rbtn_Category.IsChecked == true)
{
category.Name = Name;
Task<int> tasks = ProductDB.SaveChangesAsync();
if (tasks == null)
{
LogWrite("修改分类失败!");
}
else
{
LogWrite($"修改分类成功:ID={category.CategoryId}, Name={category.Name}");
}
}
else
{
product.Name = Name;
Task<int> tasks = ProductDB.SaveChangesAsync();
if (tasks == null)
{
LogWrite("修改产品失败!");
}
else
{
LogWrite($"修改产品成功:CategoryId={product.CategoryId},ID={product.ProductId}, Name={product.Name}");
}
}
RefreshItem();
3.2.7 删除数据
bash
GetInfo();
Button_Query_Click(sender, e);
if (rbtn_Category.IsChecked == true)
{
// 异步查询方法
ProductDB.Categories.Remove(category);
Task<int> tasks = ProductDB.SaveChangesAsync();
if (tasks == null)
{
LogWrite("删除分类失败!");
}
else
{
LogWrite($"删除分类成功:ID={ID}, Name={Name}");
}
}
else
{
// 异步查询方法
ProductDB.Products.Remove(product);
Task<int> tasks = ProductDB.SaveChangesAsync();
if (tasks == null)
{
LogWrite("修改产品失败!");
}
else
{
LogWrite($"删除产品成功:CategoryId={product.CategoryId},ID={product.ProductId}, Name={product.Name}");
}
}
RefreshItem();
3.3 数据库迁移
3.3.1 修改具体的模型实体类
我这边以Product为例,添加一个新的字段Remark。
csharp
public class Product
{
/// <summary>
/// 产品Id
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ProductId { get; set; }
/// <summary>
/// 产品名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 备注 <- 这是新增的字段
/// </summary>
public string Remark { get; set; } = string.Empty;
/// <summary>
/// 类别Id
/// </summary>
public int CategoryId { get; set; }
/// <summary>
/// 类别
/// </summary>
public virtual Category Category { get; set; }
}
3.3.2 创建迁移 (Add-Migration)
VS 的程序包管理器控制台(PMC) 或使用 .NET CLI(命令行)
PMC下创建
打开PMC。

输入创建指令:
bash
# AddProductRemark是这次迁移的备注信息,会在项目文件中生成一个对应的迁移文件
PM> Add-Migration AddProductRemark
Build started...
Build succeeded.
2026/7/16 10:50:30.855 CoreEventId.SensitiveDataLoggingEnabledWarning[10400] (Microsoft.EntityFrameworkCore.Infrastructure)
Sensitive data logging is enabled. Log entries and exception messages may include sensitive application data; this mode should only be enabled during development.
To undo this action, use Remove-Migration.
PM>
这是生成的迁移文件:

CLI下迁移
打开终端

安装donet-ef工具包
注: 安装的版本需要与Entity Framework Core的版本保持一致。
bash
PS D:\Users\LJM\Desktop\TestWpfEfCoreMySql\TestWpfEfCoreMySql> dotnet tool install --global dotnet-ef --version 9.0.0
可使用以下命令调用工具: dotnet-ef
已成功安装工具"dotnet-ef"(版本"9.0.0")。
创建迁移
注意: df工具需要在项目入口文件所在的文件夹进行操作,这个要注意下自己工程数据库项目文件入口所在的文件夹,用cd指令切到对应文件夹。
bash
PS D:\Users\LJM\Desktop\TestWpfEfCoreMySql\TestWpfEfCoreMySql> cd .\TestWpfEfCoreMySql\
PS D:\Users\LJM\Desktop\TestWpfEfCoreMySql\TestWpfEfCoreMySql\TestWpfEfCoreMySql> dotnet-ef migrations add AddProductRemark
Build started...
Build succeeded.
warn: 2026/7/16 11:01:50.968 CoreEventId.SensitiveDataLoggingEnabledWarning[10400] (Microsoft.EntityFrameworkCore.Infrastructure)
Sensitive data logging is enabled. Log entries and exception messages may include sensitive application data; this mode should only be enabled during development.
Done. To undo this action, use 'ef migrations remove'
PS D:\Users\LJM\Desktop\TestWpfEfCoreMySql\TestWpfEfCoreMySql\TestWpfEfCoreMySql>
3.3.3 迁移数据库
应用内调用函数进行迁移
这种方式建议设计开发的时候用,生产环境,建议审核后用命令行升级。
bash
private async void Migrate()
{
// 获取待执行的迁移列表
var pendingMigrations = await ProductDB.Database.GetPendingMigrationsAsync();
LogWrite(string.Format("发现 {0} 个待执行的数据库更新", pendingMigrations.Count()));
if (pendingMigrations.Any())
{
await ProductDB.Database.MigrateAsync();
LogWrite("数据库更新完成!");
}
}

CLI迁移
在项目目录下执行update指令进行迁移
bash
PS D:\Users\LJM\Desktop\TestWpfEfCoreMySql\TestWpfEfCoreMySql\TestWpfEfCoreMySql> dotnet-ef database update
3.3.4 删除迁移
在项目文件下执行remove,可以删除当前未执行的迁移,然后可以修改后重新创建一个迁移文件。
bash
dotnet-ef migrations remove
