C#学习笔记-Entity Framework Core基础操作学习
文章目录一、概述二、使用NuGet安装2.1 新建一个WPF项目2.2 安装Entity Framework Core包三、基本使用3.1 创建数据库3.1.1 创建两个模型类对应数据库的两个表3.1.2 创建上下文对象3.1.3 工程中主页面设计3.1.4 创建数据库直接使用EnsureCreated方法创建数据库使用MigrateAsync来创建数据库3.1.4 运行程序创建数据库3.2 增删改查3.2.1 调整界面3.2.2 增加绑定数据源并绑定到界面中3.2.3 查询表数据操作3.2.4 查询单条数据3.2.5 增加数据3.2.6 修改数据3.2.7 删除数据3.3 数据库迁移3.3.1 修改具体的模型实体类3.3.2 创建迁移 (Add-Migration)PMC下创建CLI下迁移3.3.3 迁移数据库应用内调用函数进行迁移CLI迁移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.02.2 安装Entity Framework Core包需要安装的包如下Entity FrameworkCore.xxx(具体数据的包)Microsoft.EntityFrameworkCore.Tools工具包Microsoft.EntityFrameworkCore.Proxies延迟加载相关注各个包安装的版本需要保持一致不然会出现冲突。打开NuGet包管理器搜索“Entity FrameworkCore.Mysql”这个按照自己使用的数据库加尾缀直接搜也可以搜索“Entity FrameworkCore”然后再找自己需要的数据库对应的包mysql的包主要有两个对比如下特性Pomelo.EntityFrameworkCore.MySqlMySql.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.30MySQL9.0.0需要仔细核对对应关系建议大多数情况选择 Pomelo无论是你正在开发的 WPF 自动化检测项目还是其他 Web 应用、微服务Pomelo.EntityFrameworkCore.MySql都是社区公认的首选。它更快的迭代速度和更活跃的社区支持能让你更方便地使用 EF Core 的新特性遇到问题也更容易找到解决方案。微软官方文档也将它列为 MySQL 的主要提供程序之一。少数情况选择官方包只有在项目有硬性要求必须使用 Oracle 官方组件时如特定的合规需求才需要考虑MySql.EntityFrameworkCore。同时需要注意选择它意味着可能需要忍受较慢的更新节奏和潜在的兼容性问题。我这边安装Pomelo的包直接装最新版9.0.0。安装时会自动安装相关的依赖库。三、基本使用3.1 创建数据库3.1.1 创建两个模型类对应数据库的两个表///summary/// 类别 ////summarypublic class Category{///summary/// 类别Id ////summarypublic int CategoryId{get;set;}///summary/// 类别名称 ////summarypublic string Name{get;set;}///summary/// 类别下的产品集合 ////summarypublic virtual ICollectionProductProducts{get;privateset;}new ObservableCollectionProduct();}public class Product{///summary/// 产品Id ////summarypublic int ProductId{get;set;}///summary/// 产品名称 ////summarypublic string Name{get;set;}///summary/// 类别Id ////summarypublic int CategoryId{get;set;}///summary/// 类别 ////summarypublic virtual Category Category{get;set;}}3.1.2 创建上下文对象上下文对象模型映射是通过DBSet来映射的相当于一个数据集。理解上可以理解成ADO的DataSet比较类似。通过重写OnConfiguring函数配置数据库连接字符串这边连接本地测试的mysql数据库。///summary/// 产品数据库上下文对象 ////summarypublic class ProductContext:DbContext{///summary/// 产品集合 ////summarypublic DbSetProductProducts{get;set;}///summary/// 类别集合 ////summarypublic DbSetCategoryCategories{get;set;}///summary/// 配置数据库上下文对象 ////summary///paramnameoptionsBuilder/paramprotected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){var connectionStringserverlocalhost;userroot;passwordroot;databasetest;var serverVersionnew 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用来展示日志后台代码封装了一个简单的日志打印函数正确的黑色失败红色。private void LogWrite(string msg){Paragraph paragraph(Paragraph)rtxt_Log.Document.Blocks.FirstBlock;Run runnew Run(string.Format({0} : {1}, DateTime.Now.ToString(HH:mm:ss), msg)){Foregroundmsg.Contains(失败)? new SolidColorBrush(Colors.Red):new SolidColorBrush(Colors.Black)};paragraph.Inlines.Add(run);paragraph.Inlines.Add(new LineBreak());}创建数据库按钮事件直接通过命令创建数据库3.1.4 创建数据库直接使用EnsureCreated方法创建数据库注测试时可用官方推荐生产环境需要时候在程序外进行迁移升级比较安全。使用此方法会与迁移功能冲突应为直接创建时不会在数据库生成迁移记录表等后续使用迁移功能时会报错需要手动处理迁移数据记录才能正常迁移。///summary/// 创建数据库按钮点击事件 ////summary///paramnamesender/param///paramnamee/paramprivate void Button_Click(object sender, RoutedEventArgs e){// 创建数据库if(ProductDB.Database.EnsureCreated()){LogWrite(创建数据库成功);}else{LogWrite(创建数据库失败);}}使用MigrateAsync来创建数据库这是使用迁移功能来创建数据库迁移时如果不存在数据库会自动创建也会在数据库创建迁移记录表用作标识后面可以正常完成数据库迁移功能不用手动处理数据。///summary/// 创建数据库 ////summaryprivate async voidCreateDatabase(){// 迁移数据库 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 增加绑定数据源并绑定到界面中创建资源Window.ResourcesCollectionViewSourcex:KeycategoryViewSource/CollectionViewSourcex:KeycategoryProductsViewSourceSource{Binding Products, Source{StaticResource categoryViewSource}}//Window.Resources设置分类表格式并绑定数据DataGridx:Namegrid_CategoriesGrid.Column0AutoGenerateColumnsFalseItemsSource{Binding Source{StaticResource categoryViewSource}}RowDetailsVisibilityModeVisibleWhenSelectedDataGrid.ColumnsDataGridTextColumnBinding{Binding CategoryId}HeaderCategory IdMinWidth80WidthSizeToHeaderIsReadOnlyTrueVisibilityVisible/DataGridTextColumnBinding{Binding Name}HeaderNameWidth*MinWidth80//DataGrid.Columns/DataGrid设置产品表格式并绑定数据DataGridx:Namegrid_ProductsGrid.Column1AutoGenerateColumnsFalseEnableRowVirtualizationTrueItemsSource{Binding Source{StaticResource categoryProductsViewSource}}RowDetailsVisibilityModeVisibleWhenSelectedDataGrid.ColumnsDataGridTextColumnBinding{Binding CategoryId}HeaderCategory IdWidthSizeToHeaderIsReadOnlyTrue/DataGridTextColumnBinding{Binding ProductId}HeaderProduct IdWidthSizeToHeaderIsReadOnlyTrue/DataGridTextColumnBinding{Binding Name}HeaderNameWidth*//DataGrid.Columns/DataGrid3.2.3 查询表数据操作查询数据按钮增加查询事件处理函数。if(categoryViewSource.Sourcenull){grid_Categories.Items.Clear();grid_Products.Items.Clear();}// 加载数据ProductDB.Categories.Load();LogWrite(分类表数据加载完毕);// 绑定集合数据categoryViewSource.Sourcenull;categoryViewSource.SourceProductDB.Categories.Local.ToObservableCollection();RefreshItem();if(categoryViewSource.Sourcenull){LogWrite(分类表集合为空);}else{LogWrite(分类表集合加载成功);}3.2.4 查询单条数据在3.1的基础上添加一些数据GetInfo();if(rbtn_Category.IsCheckedtrue){if(ID!null){// 异步查询方法 categoryProductDB.Categories.Find(ID);if(categorynull){LogWrite(未找到该分类);}else{txt_Name.Textcategory.Name;LogWrite($查询到分类ID{category.CategoryId}, Name{category.Name});}}else{// 异步查询方法 categoryProductDB.Categories.FirstOrDefault(xx.Name.Equals(Name));if(categorynull){LogWrite(未找到该分类);}else{txt_ID.Textcategory.CategoryId.ToString();LogWrite($查询到分类ID{category.CategoryId}, Name{category.Name});}}}else{if(ID!null){// 异步查询方法 productProductDB.Products.Find(ID);if(productnull){LogWrite(未找到该产品);}else{txt_Name.Textproduct.Name;LogWrite($查询到产品ID{product.ProductId}, Name{product.Name});}}else{// 异步查询方法 productProductDB.Products.FirstOrDefault(xx.Name.Equals(Name));if(productnull){LogWrite(未找到该产品);}else{txt_ID.Textproduct.ProductId.ToString();LogWrite($查询到产品ID{product.ProductId}, Name{product.Name});}}}RefreshItem();3.2.5 增加数据private async voidAdd(){GetInfo();IDnull;// 添加数据时ID应为空if(rbtn_Category.IsCheckedtrue){categorynewCategory(){NameName,};// 异步查询方法 ProductDB.Add(category);// 这边使用异步方法需要搭配await关键字添加数据后会自动更新ID。 await ProductDB.SaveChangesAsync();if(category.CategoryId0){LogWrite(添加分类失败);}else{LogWrite($添加分类成功ID{category.CategoryId}, Name{category.Name});}}else{productnewProduct(){NameName, CategoryIdcategory?.CategoryId ??0,};// 异步查询方法 ProductDB.Add(product);await ProductDB.SaveChangesAsync();if(product.ProductId0){LogWrite(添加产品失败);}else{LogWrite($添加产品成功CategoryId{product.CategoryId},ID{product.ProductId}, Name{product.Name});}}RefreshItem();}3.2.6 修改数据GetInfo();if(rbtn_Category.IsCheckedtrue){category.NameName;TaskinttasksProductDB.SaveChangesAsync();if(tasksnull){LogWrite(修改分类失败);}else{LogWrite($修改分类成功ID{category.CategoryId}, Name{category.Name});}}else{product.NameName;TaskinttasksProductDB.SaveChangesAsync();if(tasksnull){LogWrite(修改产品失败);}else{LogWrite($修改产品成功CategoryId{product.CategoryId},ID{product.ProductId}, Name{product.Name});}}RefreshItem();3.2.7 删除数据GetInfo();Button_Query_Click(sender, e);if(rbtn_Category.IsCheckedtrue){// 异步查询方法 ProductDB.Categories.Remove(category);TaskinttasksProductDB.SaveChangesAsync();if(tasksnull){LogWrite(删除分类失败);}else{LogWrite($删除分类成功ID{ID}, Name{Name});}}else{// 异步查询方法 ProductDB.Products.Remove(product);TaskinttasksProductDB.SaveChangesAsync();if(tasksnull){LogWrite(修改产品失败);}else{LogWrite($删除产品成功CategoryId{product.CategoryId},ID{product.ProductId}, Name{product.Name});}}RefreshItem();3.3 数据库迁移3.3.1 修改具体的模型实体类我这边以Product为例添加一个新的字段Remark。publicclassProduct{/// summary/// 产品Id/// /summary[Key][DatabaseGenerated(DatabaseGeneratedOption.Identity)]publicintProductId{get;set;}/// summary/// 产品名称/// /summarypublicstringName{get;set;}/// summary/// 备注 - 这是新增的字段/// /summarypublicstringRemark{get;set;}string.Empty;/// summary/// 类别Id/// /summarypublicintCategoryId{get;set;}/// summary/// 类别/// /summarypublicvirtualCategoryCategory{get;set;}}3.3.2 创建迁移 (Add-Migration)VS 的程序包管理器控制台PMC 或使用 .NET CLI命令行PMC下创建打开PMC。输入创建指令# AddProductRemark是这次迁移的备注信息会在项目文件中生成一个对应的迁移文件PMAdd-Migration AddProductRemark Build started... Build succeeded.2026/7/1610: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的版本保持一致。PS D:\Users\LJM\Desktop\TestWpfEfCoreMySql\TestWpfEfCoreMySqldotnet toolinstall--globaldotnet-ef--version9.0.0 可使用以下命令调用工具: dotnet-ef 已成功安装工具“dotnet-ef”(版本“9.0.0”)。创建迁移注意df工具需要在项目入口文件所在的文件夹进行操作这个要注意下自己工程数据库项目文件入口所在的文件夹用cd指令切到对应文件夹。PS D:\Users\LJM\Desktop\TestWpfEfCoreMySql\TestWpfEfCoreMySqlcd.\TestWpfEfCoreMySql\PS D:\Users\LJM\Desktop\TestWpfEfCoreMySql\TestWpfEfCoreMySql\TestWpfEfCoreMySqldotnet-ef migrationsaddAddProductRemark Build started... Build succeeded. warn:2026/7/1611: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, useef migrations removePS D:\Users\LJM\Desktop\TestWpfEfCoreMySql\TestWpfEfCoreMySql\TestWpfEfCoreMySql3.3.3 迁移数据库应用内调用函数进行迁移这种方式建议设计开发的时候用生产环境建议审核后用命令行升级。private async voidMigrate(){// 获取待执行的迁移列表 var pendingMigrationsawait ProductDB.Database.GetPendingMigrationsAsync();LogWrite(string.Format(发现 {0} 个待执行的数据库更新, pendingMigrations.Count()));if(pendingMigrations.Any()){await ProductDB.Database.MigrateAsync();LogWrite(数据库更新完成);}}CLI迁移在项目目录下执行update指令进行迁移PS D:\Users\LJM\Desktop\TestWpfEfCoreMySql\TestWpfEfCoreMySql\TestWpfEfCoreMySqldotnet-ef database update3.3.4 删除迁移在项目文件下执行remove可以删除当前未执行的迁移然后可以修改后重新创建一个迁移文件。dotnet-ef migrations remove