Appearance
EF Core 核心特性速览
一文了解 EF Core 的主要功能和技术亮点
📋 目录
🌍 跨平台与多数据库支持
跨平台运行
EF Core 可在所有主流操作系统上运行:
bash
# Windows
dotnet run
# Linux (Ubuntu/CentOS/Debian)
dotnet run
# macOS
dotnet run
# Docker
docker run my-efcore-app支持的数据库提供程序
官方提供程序
| 数据库 | NuGet 包 | 成熟度 |
|---|---|---|
| SQL Server | Microsoft.EntityFrameworkCore.SqlServer | ⭐⭐⭐⭐⭐ |
| SQLite | Microsoft.EntityFrameworkCore.Sqlite | ⭐⭐⭐⭐⭐ |
| Cosmos DB | Microsoft.EntityFrameworkCore.Cosmos | ⭐⭐⭐⭐ |
| InMemory | Microsoft.EntityFrameworkCore.InMemory | ⭐⭐⭐⭐(测试用) |
社区提供程序
| 数据库 | NuGet 包 | 成熟度 |
|---|---|---|
| PostgreSQL | Npgsql.EntityFrameworkCore.PostgreSQL | ⭐⭐⭐⭐⭐ |
| MySQL | Pomelo.EntityFrameworkCore.MySql | ⭐⭐⭐⭐⭐ |
| Oracle | Oracle.EntityFrameworkCore | ⭐⭐⭐⭐ |
| Firebird | FirebirdSql.EntityFrameworkCore.Firebird | ⭐⭐⭐ |
配置示例
csharp
// SQL Server
options.UseSqlServer("Server=localhost;Database=MyDb;Trusted_Connection=True");
// PostgreSQL
options.UseNpgsql("Host=localhost;Database=MyDb;Username=postgres;Password=pass");
// MySQL
options.UseMySql("Server=localhost;Database=MyDb;User=root;Password=pass",
ServerVersion.AutoDetect("Server=localhost;..."));
// SQLite
options.UseSqlite("DataSource=mydb.db");
// Cosmos DB
options.UseCosmos(
"https://localhost:8081",
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
"MyDatabase");数据库无关的代码
csharp
// 相同的 LINQ 代码可在不同数据库上运行
var users = await context.Users
.Where(u => u.Age > 18)
.OrderBy(u => u.Name)
.ToListAsync();
// EF Core 自动翻译为对应数据库的 SQL 方言🔍 LINQ 查询系统
强类型查询
csharp
// 编译时检查,避免运行时错误
var products = await context.Products
.Where(p => p.Price > 100) // 如果 Price 不存在,编译失败
.OrderByDescending(p => p.Price)
.Take(10)
.ToListAsync();复杂的 LINQ 表达式
csharp
// 联接查询
var result = await from o in context.Orders
join c in context.Customers on o.CustomerId equals c.Id
where o.TotalAmount > 1000
select new
{
OrderId = o.Id,
CustomerName = c.Name,
Amount = o.TotalAmount
};
// 分组聚合
var stats = await context.Products
.GroupBy(p => p.Category)
.Select(g => new
{
Category = g.Key,
Count = g.Count(),
AvgPrice = g.Average(p => p.Price),
MaxPrice = g.Max(p => p.Price)
})
.ToListAsync();
// 子查询
var expensiveProducts = await context.Products
.Where(p => p.Price > context.Products.Average(x => x.Price))
.ToListAsync();导航属性查询
csharp
// Include - 预加载关联数据
var orders = await context.Orders
.Include(o => o.Customer)
.Include(o => o.Items)
.ThenInclude(i => i.Product)
.Where(o => o.OrderDate >= DateTime.Today)
.ToListAsync();
// 过滤 Include(EF Core 5.0+)
var orders = await context.Orders
.Include(o => o.Items.Where(i => i.Quantity > 10))
.ToListAsync();📊 变更跟踪
自动状态检测
csharp
var product = await context.Products.FindAsync(1);
// EF Core 自动跟踪变化
product.Price = 129.99m; // 检测到修改
product.Name = "New Name"; // 检测到修改
// 只更新修改的字段
await context.SaveChangesAsync();
// UPDATE Products SET Price = 129.99, Name = 'New Name' WHERE Id = 1实体状态
csharp
// Added - 新增
context.Products.Add(newProduct);
// 状态: EntityState.Added
// Modified - 修改
product.Price = 99.99m;
// 状态: EntityState.Modified
// Deleted - 删除
context.Products.Remove(product);
// 状态: EntityState.Deleted
// Unchanged - 未改变
var product = await context.Products.FindAsync(1);
// 状态: EntityState.Unchanged无跟踪查询(性能优化)
csharp
// 只读场景使用 AsNoTracking - 性能提升 20-50%
var products = await context.Products
.AsNoTracking()
.Where(p => p.IsActive)
.ToListAsync();
// 上下文级别设置
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;🔄 迁移系统
代码优先的数据库版本控制
bash
# 创建迁移
dotnet ef migrations add AddUserEmail
# 应用迁移
dotnet ef database update
# 回滚迁移
dotnet ef database update PreviousMigration
# 生成 SQL 脚本
dotnet ef migrations script迁移文件结构
Migrations/
├── 20240101120000_InitialCreate.cs # 迁移文件
├── 20240101120000_InitialCreate.Designer.cs # 元数据
└── AppDbContextModelSnapshot.cs # 当前模型快照迁移代码示例
csharp
public partial class AddUserEmail : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Email",
table: "Users",
type: "nvarchar(100)",
maxLength: 100,
nullable: false,
defaultValue: "");
migrationBuilder.CreateIndex(
name: "IX_Users_Email",
table: "Users",
column: "Email",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_Users_Email",
table: "Users");
migrationBuilder.DropColumn(
name: "Email",
table: "Users");
}
}🔗 关系映射
一对一(One-to-One)
csharp
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
// 导航属性
public Address Address { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public string City { get; set; }
// 外键
public int CustomerId { get; set; }
public Customer Customer { get; set; }
}
// 配置
modelBuilder.Entity<Customer>(entity =>
{
entity.HasOne(c => c.Address)
.WithOne(a => a.Customer)
.HasForeignKey<Address>(a => a.CustomerId);
});一对多(One-to-Many)
csharp
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
// 导航属性
public Customer Customer { get; set; }
public ICollection<OrderItem> Items { get; set; }
}
public class OrderItem
{
public int Id { get; set; }
public int OrderId { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; }
// 导航属性
public Order Order { get; set; }
public Product Product { get; set; }
}
// 配置
modelBuilder.Entity<Order>(entity =>
{
entity.HasMany(o => o.Items)
.WithOne(i => i.Order)
.HasForeignKey(i => i.OrderId)
.OnDelete(DeleteBehavior.Cascade);
});多对多(Many-to-Many)
csharp
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
// 导航属性
public ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public int Id { get; set; }
public string Name { get; set; }
// 导航属性
public ICollection<Product> Products { get; set; }
}
// 配置(EF Core 5.0+ 简化)
modelBuilder.Entity<Product>()
.HasMany(p => p.Tags)
.WithMany(t => t.Products);⚡ 异步编程
完整的 async/await 支持
csharp
// 异步查询
var products = await context.Products
.Where(p => p.Price > 100)
.ToListAsync();
// 异步单个
var product = await context.Products
.FirstOrDefaultAsync(p => p.Id == id);
// 异步计数
var count = await context.Products
.CountAsync(p => p.IsActive);
// 异步保存
context.Products.Add(newProduct);
await context.SaveChangesAsync();
// 异步批量操作(EF Core 8+)
await context.Products
.Where(p => p.IsObsolete)
.ExecuteDeleteAsync();Web API 中的异步
csharp
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly AppDbContext _context;
public ProductsController(AppDbContext context)
{
_context = context;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<Product>>> GetProducts()
{
return await _context.Products.ToListAsync();
}
[HttpGet("{id}")]
public async Task<ActionResult<Product>> GetProduct(int id)
{
var product = await _context.Products.FindAsync(id);
if (product == null)
return NotFound();
return product;
}
[HttpPost]
public async Task<ActionResult<Product>> CreateProduct(Product product)
{
_context.Products.Add(product);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetProduct),
new { id = product.Id }, product);
}
}🚀 性能优化
1. 批量操作(EF Core 8+)
csharp
// 批量删除
await context.Products
.Where(p => p.IsObsolete)
.ExecuteDeleteAsync();
// 批量更新
await context.Products
.Where(p => p.Category == "Old")
.ExecuteUpdateAsync(setters => setters
.SetProperty(p => p.Category, "New"));性能对比:
- 传统方式:1000 条记录 = 1001 次数据库往返
- 批量操作:1000 条记录 = 1 次数据库往返
- 提升 1000 倍
2. 预编译查询(EF Core 8+)
csharp
private static readonly Func<AppDbContext, int, Task<Product?>> GetProductById =
EF.CompileAsyncQuery(
(AppDbContext context, int id) =>
context.Products.FirstOrDefault(p => p.Id == id));
// 使用(跳过 LINQ 翻译,直接执行)
var product = await GetProductById(context, productId);性能提升:10-30%(高频查询场景)
3. 投影查询
csharp
// 只查询需要的列
var products = await context.Products
.Where(p => p.IsActive)
.Select(p => new ProductDto
{
Id = p.Id,
Name = p.Name,
Price = p.Price
// 不加载其他字段
})
.ToListAsync();4. 分页查询
csharp
var pageNumber = 1;
var pageSize = 20;
var products = await context.Products
.OrderBy(p => p.Name)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();🎁 高级特性
全局查询过滤器
csharp
// 软删除
modelBuilder.Entity<Product>(entity =>
{
entity.HasQueryFilter(p => !p.IsDeleted);
});
// 多租户
modelBuilder.Entity<Order>(entity =>
{
entity.HasQueryFilter(o => o.TenantId == currentTenantId);
});
// 自动应用,无需在每个查询中添加
var products = await context.Products.ToListAsync();
// 自动生成: WHERE IsDeleted = 0值转换器
csharp
// 枚举存储为字符串
modelBuilder.Entity<Product>(entity =>
{
entity.Property(p => p.Status)
.HasConversion<string>();
});
// 自定义转换
modelBuilder.Entity<User>(entity =>
{
entity.Property(u => u.PhoneNumber)
.HasConversion(
v => new PhoneNumber(v), // 写入时转换
v => v.ToString()); // 读取时转换
});影子属性
csharp
// 不在实体类中定义的属性
modelBuilder.Entity<Order>(entity =>
{
entity.Property<DateTime>("CreatedAt");
entity.Property<string>("LastModifiedBy");
});
// 使用
var order = new Order();
context.Entry(order).Property("CreatedAt").CurrentValue = DateTime.UtcNow;继承映射
TPH(Table-per-Hierarchy)- 默认
csharp
public class Animal
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Dog : Animal
{
public string Breed { get; set; }
}
public class Cat : Animal
{
public bool IsIndoor { get; set; }
}
// 所有类型存储在一张表中,通过 Discriminator 列区分TPT(Table-per-Type)- EF Core 7+
csharp
[Table("Animals")]
public class Animal { ... }
[Table("Dogs")]
public class Dog : Animal { ... }
[Table("Cats")]
public class Cat : Animal { ... }
// 每个类型一张表,通过 JOIN 查询JSON 列支持(EF Core 8+)
csharp
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
// 复杂对象存储为 JSON
public ProductDetails Details { get; set; }
}
public class ProductDetails
{
public decimal Weight { get; set; }
public string Color { get; set; }
public Dictionary<string, string> Attributes { get; set; }
}
// 配置
modelBuilder.Entity<Product>(entity =>
{
entity.OwnsOne(p => p.Details, owned =>
{
owned.ToJson(); // 存储为 JSON 列
});
});
// 查询
var products = await context.Products
.Where(p => p.Details.Weight > 10)
.ToListAsync();🛠️ 开发工具
CLI 命令
bash
# 安装工具
dotnet tool install --global dotnet-ef
# 迁移管理
dotnet ef migrations add <name>
dotnet ef database update
dotnet ef migrations list
# 逆向工程
dotnet ef dbcontext scaffold "连接字符串" Microsoft.EntityFrameworkCore.SqlServer
# 诊断
dotnet ef dbcontext info日志记录
csharp
// 启用详细日志
options.LogTo(
Console.WriteLine,
new[] { DbLoggerCategory.Database.Command.Name },
LogLevel.Information);
// 查看生成的 SQL
var sql = context.Products
.Where(p => p.Price > 100)
.ToQueryString();📊 特性对比总结
EF Core vs EF 6
| 特性 | EF Core | EF 6 |
|---|---|---|
| 跨平台 | ✅ | ❌ |
| 性能 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 批量操作 | ✅ 原生 | ❌ 第三方 |
| JSON 列 | ✅ | ❌ |
| 预编译查询 | ✅ | ⚠️ 复杂 |
| 新特性 | ✅ 持续更新 | ❌ 维护模式 |
.NET 版本对比
| 特性 | 8.0 LTS | 9.0 STS | 10.0 预览 |
|---|---|---|---|
| 批量操作 | ✅ | ✅ | ✅ |
| JSON 列 | ✅ | ✅ 增强 | ✅ 完整 |
| AI 优化 | ❌ | ❌ | 🚧 |
| 稳定性 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
💡 最佳实践速查
始终使用异步方法
csharpawait context.Products.ToListAsync(); // ✅ context.Products.ToList(); // ❌只读查询使用 AsNoTracking
csharpcontext.Products.AsNoTracking().ToList(); // ✅避免 N+1 查询
csharpcontext.Orders.Include(o => o.Items).ToList(); // ✅ foreach (var order in orders) { order.Items... } // ❌使用投影减少数据传输
csharpSelect(p => new Dto { Id = p.Id, Name = p.Name }); // ✅批量操作用 ExecuteUpdate/Delete
csharpExecuteUpdateAsync(...); // ✅ foreach + SaveChangesAsync // ❌
📚 延伸阅读
提示:本页面是核心特性的快速概览,详细内容请参考各专题文档。