Entity Framework Core 指南
2024/2/15大约 3 分钟
Entity Framework Core 指南
概述
Entity Framework Core (EF Core) 是一个轻量级、可扩展、开源的对象关系映射 (ORM) 框架,用于 .NET 应用程序。
1. 入门配置
1.1 安装依赖
# 安装 EF Core
dotnet add package Microsoft.EntityFrameworkCore
# 安装数据库提供者(以 SQL Server 为例)
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
# 安装工具
dotnet tool install --global dotnet-ef1.2 创建 DbContext
public class AppDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=localhost;Database=MyDb;Trusted_Connection=True;");
}
}
// 实体类
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Product> Products { get; set; }
}1.3 依赖注入配置
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}2. 数据迁移
2.1 创建迁移
# 创建初始迁移
dotnet ef migrations add InitialCreate
# 更新数据库
dotnet ef database update
# 创建新迁移
dotnet ef migrations add AddProductDescription
# 回滚迁移
dotnet ef migrations remove2.2 迁移文件结构
Migrations/
├── 20240101000000_InitialCreate.cs
├── 20240102000000_AddProductDescription.cs
└── AppDbContextModelSnapshot.cs3. 查询数据
3.1 基本查询
// 查询所有
var products = await _context.Products.ToListAsync();
// 条件查询
var cheapProducts = await _context.Products
.Where(p => p.Price < 100)
.ToListAsync();
// 单条查询
var product = await _context.Products
.FirstOrDefaultAsync(p => p.Id == id);3.2 关联数据查询
// 包含关联数据(Eager Loading)
var productsWithCategory = await _context.Products
.Include(p => p.Category)
.ToListAsync();
// 多级关联
var orders = await _context.Orders
.Include(o => o.Customer)
.Include(o => o.OrderItems)
.ThenInclude(oi => oi.Product)
.ToListAsync();
// 懒加载(需要安装 LazyLoadingProxies)
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString)
.UseLazyLoadingProxies());3.3 投影查询
// 选择特定字段
var productNames = await _context.Products
.Select(p => p.Name)
.ToListAsync();
// 匿名类型
var productInfo = await _context.Products
.Select(p => new { p.Id, p.Name, p.Price })
.ToListAsync();
// DTO 投影
var dtos = await _context.Products
.Select(p => new ProductDto
{
Id = p.Id,
Name = p.Name,
Price = p.Price,
CategoryName = p.Category.Name
})
.ToListAsync();4. 数据操作
4.1 添加数据
// 添加单条
var product = new Product { Name = "iPhone", Price = 5999 };
_context.Products.Add(product);
await _context.SaveChangesAsync();
// 添加多条
var products = new List<Product>
{
new Product { Name = "iPad", Price = 3999 },
new Product { Name = "MacBook", Price = 9999 }
};
_context.Products.AddRange(products);
await _context.SaveChangesAsync();4.2 更新数据
// 方法1:先查询再更新
var product = await _context.Products.FindAsync(id);
if (product != null)
{
product.Price = 6999;
await _context.SaveChangesAsync();
}
// 方法2:直接更新(无需查询)
_context.Products.Update(product);
await _context.SaveChangesAsync();
// 批量更新
await _context.Products
.Where(p => p.CategoryId == 1)
.ExecuteUpdateAsync(s => s.SetProperty(p => p.Price, p => p.Price * 1.1m));4.3 删除数据
// 方法1:先查询再删除
var product = await _context.Products.FindAsync(id);
if (product != null)
{
_context.Products.Remove(product);
await _context.SaveChangesAsync();
}
// 批量删除
await _context.Products
.Where(p => p.Price < 100)
.ExecuteDeleteAsync();5. 高级查询
5.1 分页查询
int pageNumber = 1;
int pageSize = 10;
var pagedProducts = await _context.Products
.OrderBy(p => p.Price)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();5.2 聚合查询
// 统计
var count = await _context.Products.CountAsync();
var avgPrice = await _context.Products.AverageAsync(p => p.Price);
var maxPrice = await _context.Products.MaxAsync(p => p.Price);
// 分组统计
var categoryStats = await _context.Products
.GroupBy(p => p.CategoryId)
.Select(g => new
{
CategoryId = g.Key,
ProductCount = g.Count(),
AvgPrice = g.Average(p => p.Price)
})
.ToListAsync();5.3 原生 SQL 查询
// 查询
var products = await _context.Products
.FromSqlRaw("SELECT * FROM Products WHERE Price > {0}", 100)
.ToListAsync();
// 执行命令
await _context.Database.ExecuteSqlRawAsync(
"UPDATE Products SET Price = Price * 1.1 WHERE CategoryId = {0}", categoryId);6. 性能优化
6.1 跟踪与非跟踪
// 跟踪查询(默认)- 支持更改检测
var tracked = await _context.Products.FirstAsync(p => p.Id == id);
// 非跟踪查询 - 只读场景性能更好
var untracked = await _context.Products.AsNoTracking().FirstAsync(p => p.Id == id);6.2 编译查询
// 定义编译查询
private static readonly Func<AppDbContext, int, Product> GetProductById =
EF.CompileQuery((AppDbContext context, int id) =>
context.Products.FirstOrDefault(p => p.Id == id));
// 使用
var product = GetProductById(_context, id);6.3 查询优化建议
- 使用 Include 而非多次查询
- 避免 N+1 查询问题
- 使用 AsNoTracking 进行只读操作
- 编译重复执行的查询
- 合理使用索引
7. 并发控制
7.1 乐观并发
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
// 并发令牌
[Timestamp]
public byte[] RowVersion { get; set; }
}
// 更新时检查并发
try
{
var product = await _context.Products.FindAsync(id);
product.Price = newPrice;
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
// 处理并发冲突
}总结
Entity Framework Core 是 .NET 生态中最流行的 ORM 框架,掌握其核心功能对于高效开发数据库应用至关重要。
作者:Blogger
日期:2024年2月15日