最完整EF Core入门指南:从零开始掌握.NET ORM
引言
还在为繁琐的数据库操作而烦恼?还在手动编写SQL语句处理数据持久化?Entity Framework Core(EF Core)作为.NET平台最强大的对象关系映射(ORM)框架,将彻底改变你的开发体验。本文将带你从零开始,全面掌握EF Core的核心概念、使用技巧和最佳实践。
读完本文,你将获得:
- ✅ EF Core核心概念与架构理解
- ✅ 数据库上下文(DbContext)配置与使用
- ✅ 实体模型定义与关系映射
- ✅ LINQ查询与数据操作完整指南
- ✅ 迁移管理与数据库版本控制
- ✅ 性能优化与高级特性
- ✅ 实战项目示例与最佳实践
1. EF Core概述与核心概念
1.1 什么是EF Core?
Entity Framework Core是微软开发的轻量级、可扩展、开源的对象关系映射器(ORM),它允许.NET开发者使用.NET对象来处理数据库,无需关注底层数据库细节。
1.2 EF Core核心组件
| 组件 | 描述 | 作用 |
|---|---|---|
| DbContext | 数据库会话 | 管理数据库连接、跟踪变更、执行查询 |
| DbSet | 实体集合 | 表示数据库中的表 |
| Model Builder | 模型配置 | 定义实体映射关系 |
| Migration | 数据库迁移 | 管理数据库架构变更 |
2. 环境准备与安装
2.1 安装EF Core
# 安装EF Core核心包
dotnet add package Microsoft.EntityFrameworkCore
# 安装SQL Server提供程序
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
# 安装SQLite提供程序
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
# 安装工具包(用于迁移)
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Tools
2.2 项目配置
确保在.csproj文件中包含正确的目标框架:
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
3. 创建第一个DbContext
3.1 定义实体模型
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int Rating { get; set; }
// 导航属性 - 一对多关系
public List<Post> Posts { get; set; } = new List<Post>();
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime CreatedDate { get; set; }
// 外键属性
public int BlogId { get; set; }
// 导航属性 - 多对一关系
public Blog Blog { get; set; }
}
3.2 创建DbContext
using Microsoft.EntityFrameworkCore;
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// 配置SQL Server连接字符串
optionsBuilder.UseSqlServer(
"Server=(localdb)\\mssqllocaldb;Database=BloggingDb;Trusted_Connection=true;");
// 或者配置SQLite连接
// optionsBuilder.UseSqlite("Data Source=blogging.db");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 配置模型关系
modelBuilder.Entity<Blog>()
.HasMany(b => b.Posts)
.WithOne(p => p.Blog)
.HasForeignKey(p => p.BlogId);
}
}
4. 数据库迁移管理
4.1 创建初始迁移
# 创建迁移
dotnet ef migrations add InitialCreate
# 应用迁移到数据库
dotnet ef database update
4.2 迁移文件结构
4.3 常见迁移命令
| 命令 | 描述 | 示例 |
|---|---|---|
| Add Migration | 创建新迁移 | dotnet ef migrations Add AddRatingToBlog |
| Remove Migration | 删除最新迁移 | dotnet ef migrations Remove |
| Update Database | 应用迁移 | dotnet ef database update |
| Script Migration | 生成SQL脚本 | dotnet ef migrations script |
5. 数据操作CRUD
5.1 创建数据(Create)
using var context = new BloggingContext();
// 添加新博客
var blog = new Blog { Url = "https://example.com", Rating = 5 };
context.Blogs.Add(blog);
// 添加带关联帖子的博客
var blogWithPosts = new Blog
{
Url = "https://techblog.com",
Rating = 4,
Posts = new List<Post>
{
new Post { Title = "EF Core入门", Content = "..." },
new Post { Title = "高级查询技巧", Content = "..." }
}
};
context.Blogs.Add(blogWithPosts);
// 保存更改
context.SaveChanges();
5.2 查询数据(Read)
// 基本查询
var blogs = context.Blogs.ToList();
// 条件查询
var highRatedBlogs = context.Blogs
.Where(b => b.Rating > 3)
.ToList();
// 包含关联数据(Eager Loading)
var blogsWithPosts = context.Blogs
.Include(b => b.Posts)
.Where(b => b.Rating > 3)
.ToList();
// 投影查询
var blogTitles = context.Blogs
.Select(b => new { b.Url, PostCount = b.Posts.Count })
.ToList();
5.3 更新数据(Update)
// 查询并修改
var blog = context.Blogs.First();
blog.Rating = 5;
context.SaveChanges();
// 批量更新
context.Blogs
.Where(b => b.Rating < 3)
.ExecuteUpdate(b => b.SetProperty(p => p.Rating, p => p.Rating + 1));
5.4 删除数据(Delete)
// 删除单个实体
var blog = context.Blogs.First();
context.Blogs.Remove(blog);
context.SaveChanges();
// 批量删除
context.Blogs
.Where(b => b.Rating < 2)
.ExecuteDelete();
6. 高级查询技巧
6.1 LINQ查询示例
// 复杂查询
var recentPosts = context.Posts
.Where(p => p.CreatedDate > DateTime.Now.AddDays(-7))
.OrderByDescending(p => p.CreatedDate)
.Select(p => new
{
p.Title,
BlogUrl = p.Blog.Url,
DaysAgo = EF.Functions.DateDiffDay(p.CreatedDate, DateTime.Now)
})
.ToList();
// 分组查询
var postsByBlog = context.Posts
.GroupBy(p => p.Blog.Url)
.Select(g => new
{
BlogUrl = g.Key,
PostCount = g.Count(),
AverageRating = g.Average(p => p.Blog.Rating)
})
.ToList();
6.2 原始SQL查询
// 执行原始SQL
var blogs = context.Blogs
.FromSqlRaw("SELECT * FROM Blogs WHERE Rating > {0}", 3)
.ToList();
// 存储过程调用
var result = context.Blogs
.FromSqlRaw("EXEC GetTopRatedBlogs @minRating={0}", 4)
.ToList();
7. 模型配置与关系
7.1 数据注解配置
public class Blog
{
[Key]
public int BlogId { get; set; }
[Required]
[MaxLength(500)]
public string Url { get; set; }
[Range(1, 5)]
public int Rating { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedDate { get; set; }
}
7.2 Fluent API配置
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>(entity =>
{
entity.HasKey(b => b.BlogId);
entity.Property(b => b.Url)
.IsRequired()
.HasMaxLength(500);
entity.Property(b => b.Rating)
.HasDefaultValue(3);
entity.HasIndex(b => b.Url)
.IsUnique();
});
modelBuilder.Entity<Post>(entity =>
{
entity.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.HasForeignKey(p => p.BlogId)
.OnDelete(DeleteBehavior.Cascade);
});
}
7.3 关系类型配置
8. 性能优化策略
8.1 查询性能优化
// 使用AsNoTracking避免变更跟踪
var blogs = context.Blogs
.AsNoTracking()
.Where(b => b.Rating > 3)
.ToList();
// 分页查询
var pagedBlogs = context.Blogs
.OrderBy(b => b.BlogId)
.Skip(20)
.Take(10)
.ToList();
// 只选择需要的字段
var blogInfo = context.Blogs
.Select(b => new { b.BlogId, b.Url })
.ToList();
8.2 批量操作优化
// 使用批量操作
context.BulkInsert(blogs);
context.BulkUpdate(blogs);
context.BulkDelete(blogs);
// 或者使用ExecuteUpdate/ExecuteDelete
context.Blogs
.Where(b => b.Rating < 3)
.ExecuteUpdate(b => b.SetProperty(p => p.Rating, 3));
9. 实战:完整示例项目
9.1 项目结构
BloggingApp/
├── Models/
│ ├── Blog.cs
│ ├── Post.cs
│ └── Comment.cs
├── Data/
│ └── BloggingContext.cs
├── Services/
│ └── BlogService.cs
└── Program.cs
9.2 服务层实现
public class BlogService
{
private readonly BloggingContext _context;
public BlogService(BloggingContext context)
{
_context = context;
}
public async Task<Blog> CreateBlogAsync(string url, int rating)
{
var blog = new Blog { Url = url, Rating = rating };
_context.Blogs.Add(blog);
await _context.SaveChangesAsync();
return blog;
}
public async Task<List<Blog>> GetTopRatedBlogsAsync(int count)
{
return await _context.Blogs
.Where(b => b.Rating >= 4)
.OrderByDescending(b => b.Rating)
.Take(count)
.Include(b => b.Posts)
.AsNoTracking()
.ToListAsync();
}
}
9.3 依赖注入配置
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<BloggingContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<BlogService>();
var app = builder.Build();
10. 常见问题与解决方案
10.1 性能问题排查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 查询缓慢 | N+1查询问题 | 使用Include预加载关联数据 |
| 内存占用高 | 变更跟踪过多 | 使用AsNoTracking() |
| 保存缓慢 | 大量单条操作 | 使用批量操作 |
10.2 并发处理
public class Blog
{
[Timestamp]
public byte[] RowVersion { get; set; }
}
// 处理并发冲突
try
{
await context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
foreach (var entry in ex.Entries)
{
var databaseValues = await entry.GetDatabaseValuesAsync();
var currentValues = entry.CurrentValues;
// 处理冲突逻辑
foreach (var property in currentValues.Properties)
{
var databaseValue = databaseValues[property];
var currentValue = currentValues[property];
// 选择保留哪个值
currentValues[property] = databaseValue;
}
await entry.RefreshAsync(RefreshMode.StoreWins);
}
}
11. 最佳实践总结
11.1 开发阶段建议
- 使用Code First:从领域模型开始,让EF Core生成数据库
- 频繁创建迁移:每次模型变更都创建新的迁移
- 编写集成测试:确保数据库操作的正确性
- 使用配置分离:将连接字符串放在配置文件中
11.2 生产环境建议
- 启用日志记录:监控SQL查询性能
- 使用连接池:优化数据库连接管理
- 定期清理迁移:删除不再需要的旧迁移
- 备份迁移历史:确保可以回滚到任何版本
11.3 性能优化清单
- 使用AsNoTracking()减少内存占用
- 使用Include()避免N+1查询
- 使用批量操作减少数据库往返
- 创建适当的索引优化查询性能
- 监控生成的SQL语句
结语
EF Core作为.NET生态系统中最重要的数据访问技术,掌握了它就掌握了现代.NET开发的钥匙。从简单的CRUD操作到复杂的企业级应用,EF Core都能提供强大的支持。通过本文的全面学习,相信你已经具备了使用EF Core进行实际项目开发的能力。
记住,良好的数据访问层设计是应用程序成功的关键。继续探索EF Core的高级特性,如全局查询过滤器、值转换器、拦截器等,让你的数据访问层更加健壮和高效。
开始你的EF Core之旅吧,让数据访问变得简单而优雅!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



