Skip to content

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 ServerMicrosoft.EntityFrameworkCore.SqlServer⭐⭐⭐⭐⭐
SQLiteMicrosoft.EntityFrameworkCore.Sqlite⭐⭐⭐⭐⭐
Cosmos DBMicrosoft.EntityFrameworkCore.Cosmos⭐⭐⭐⭐
InMemoryMicrosoft.EntityFrameworkCore.InMemory⭐⭐⭐⭐(测试用)

社区提供程序 ​

数据库NuGet 包成熟度
PostgreSQLNpgsql.EntityFrameworkCore.PostgreSQL⭐⭐⭐⭐⭐
MySQLPomelo.EntityFrameworkCore.MySql⭐⭐⭐⭐⭐
OracleOracle.EntityFrameworkCore⭐⭐⭐⭐
FirebirdFirebirdSql.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 CoreEF 6
跨平台✅❌
性能⭐⭐⭐⭐⭐⭐⭐⭐
批量操作✅ 原生❌ 第三方
JSON 列✅❌
预编译查询✅⚠️ 复杂
新特性✅ 持续更新❌ 维护模式

.NET 版本对比 ​

特性8.0 LTS9.0 STS10.0 预览
批量操作✅✅✅
JSON 列✅✅ 增强✅ 完整
AI 优化❌❌🚧
稳定性⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

💡 最佳实践速查 ​

  1. 始终使用异步方法

    csharp
    await context.Products.ToListAsync(); // ✅
    context.Products.ToList(); // ❌
  2. 只读查询使用 AsNoTracking

    csharp
    context.Products.AsNoTracking().ToList(); // ✅
  3. 避免 N+1 查询

    csharp
    context.Orders.Include(o => o.Items).ToList(); // ✅
    foreach (var order in orders) { order.Items... } // ❌
  4. 使用投影减少数据传输

    csharp
    Select(p => new Dto { Id = p.Id, Name = p.Name }); // ✅
  5. 批量操作用 ExecuteUpdate/Delete

    csharp
    ExecuteUpdateAsync(...); // ✅
    foreach + SaveChangesAsync // ❌

📚 延伸阅读 ​


提示:本页面是核心特性的快速概览,详细内容请参考各专题文档。

基于 MIT 许可发布