商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > C#怎么使用Entity Framework Core_C# EF Core入门到实战教程【进阶】

C#怎么使用Entity Framework Core_C# EF Core入门到实战教程【进阶】

  发布于2026-07-07 阅读(0)

扫一扫,手机访问

要说清楚 EF Core 为什么用起来总遇到坑,先得明白一个基本前提:它不是那种“装个包就能查数据”的傻瓜工具。要搞定 EF Core,关键就在于模型定义、上下文配置和迁移机制这三件事得协同好,一个都不能漏。缺了哪一环,Sa veChanges() 要么悄无声息地失败,要么直接抛出一个 InvalidOperationException——绝不会给你一个你以为的数据库错误提示。

所以,别指望写完一个 public class AppDbContext : DbContext 就万事大吉。EF Core 根本不知道你要连哪个数据库,也不知道用哪种数据库驱动。如果没有在 OnConfiguring 方法里配置连接串,或者没有通过依赖注入传入配置选项,运行时会直接报 No database provider has been configured。这算是入门最常见的拦路虎。

具体怎么配?本地开发做快速验证,直接在 OnConfiguring 里调用 optionsBuilder.UseSqlite("Data Source=app.db") 或者 UseSqlServer(...) 就够用。但到了生产环境,就别这么玩硬编码了。推荐走依赖注入 + appsettings.json 的方式,既灵活又便于切换环境——比如开发用 SQLite,测试用 SQL Server,发布时改成生产数据库。还有一个细节容易被忽略:SQLite 的路径一定要确认好权限。Windows 下用 Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) 这种写法,比自己写死一个 C:\temp 路径可靠得多。

接着是实体类定义里的坑。

EF Core 默认按约定找主键:属性名叫 IdBlogId(类名加 Id),并且类型是 intlongGuid 这类。如果你写成了 public int blog_id { get; set; },它不会识别为主键。生成的迁移里就没有 PRIMARY KEY,这时候 dotnet ef migrations add 可能还能通过,但执行 dotnet ef database update 时就会卡在建表这一步。

有几种方式可以强制指定主键:用 [Key] 特性,或者在 Fluent API 里写 modelBuilder.Entity().HasKey(e => e.BlogId)。另外,string 类型默认会映射为 nvarchar(max),插入超长内容不会报错,但性能会明显下降。建议加上 [StringLength(200)],这样才会生成指定长度的 nvarchar(200) 字段。

还有个容易被忽略的点:从 EF Core 6 开始,非空引用类型字段必须要有默认值,或者显式标记为可空。否则编译能过,运行时却会报 InvalidOperationException,提示你“The property 'Name' cannot be marked as nullable”。

再来看迁移机制。很多人以为 dotnet ef migrations add 是“一键同步”数据库,其实根本不是。它只是生成一个 C# 文件,里面包含了 Up(MigrationBuilder migrationBuilder)Down(MigrationBuilder migrationBuilder) 两个方法。真正修改数据库结构的是 dotnet ef database update——它只会执行最后一次迁移的 Up 方法,而不是把所有迁移叠加执行一遍。

这块有几个常见误区要特别留意:

第一,删掉迁移文件夹再重新 add,并不会清空已有的数据库。它只会让 EF Core 认为当前的模型就是初始状态。如果后续调用 update,可能因为数据库里已存在某些表但迁移记录对不上而导致报错。

第二,修改了实体类之后,忘了 add 新迁移就直接 update,EF Core 会静默跳过,数据库结构完全不变。但你的代码里新增的表或字段是没办法正常读取和写入的。

如果你需要把迁移脚本导出到 SQL 做审核,推荐使用 dotnet ef migrations script --idempotent,而不是靠 update 在里面猜效果。

查询性能方面,最常用也最容易忽略的一个优化点是 AsNoTracking()

默认情况下,EF Core 会对每一次查询结果开启变更跟踪(Change Tracking)。这意味着你查了 1000 条 Blog 记录,EF Core 就会在内存里维护 1000 个实体快照。如果你只是展示列表、不打算修改任何数据,这完全是浪费内存和 CPU 算力。

对于只读场景,习惯性加上 context.Blogs.AsNoTracking().ToList()。做关联查询时,像查 Blog 带 Posts 这种场景,一定要用 .Include(b => b.Posts),否则会触发 N+1 查询——你查了 100 个 Blog,后续会额外发出 100 次 SQL 去查 Posts,性能简直灾难。

还有一个技巧:FindAsync() 内部是走主键索引的,并且自动启用了 AsNoTracking(仅限单条记录)。它比 Where(x => x.Id == id).FirstOrDefaultAsync() 略快一点,但语义上略有不同。两者在查不到数据时都返回 null,区别主要在缓存行为上。

最后提醒一个容易被忽视、但后果却很严重的问题:DbContext 是有生命周期的。

如果你把它当作静态单例来用,或者在 Web API 里手动 new 出来而不及时 Dispose,迟早会碰到连接池耗尽、并发修改异常或者内存泄漏。 .NET 6 之后的标准做法是注册为 Scoped,让 DbContext 随着 HTTP 请求的创建而创建,请求结束自动释放。既安全又高效。

本文转载于:https://www.php.cn/faq/2435703.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注