C# Entity Framework使用:从入门到精通


Entity Framework (EF) 是.NET平台最流行的对象关系映射(ORM)框架,它简化了数据库交互,让开发者能够以面向对象的方式操作数据。本文将全面介绍EF Core的使用方法,帮助您高效地进行数据访问开发。

一、Entity Framework Core基础

1.1 EF Core简介

Entity Framework Core是轻量级、可扩展、跨平台版的EF,主要特点包括:

  • 支持LINQ查询
  • 变更跟踪
  • 数据库迁移
  • 跨平台支持

1.2 安装与配置

通过NuGet安装核心包:

Install-Package Microsoft.EntityFrameworkCore.SqlServer

基本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=(localdb)\mssqllocaldb;Database=EfCoreDemo;Trusted_Connection=True");
    }
}

二、数据模型定义

2.1 实体类配置

基本实体类示例:

public class Product
{
    public int Id { get; set; }  // 主键
    public string Name { get; set; }
    public decimal Price { get; set; }
    public DateTime CreatedDate { get; set; } = DateTime.UtcNow;

    // 导航属性
    public int CategoryId { get; set; }
    public Category Category { get; set; }
}

2.2 数据注解配置

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

[Table("Products")]
public class Product
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ProductId { get; set; }

    [Required]
    [MaxLength(100)]
    public string Name { get; set; }

    [Column(TypeName = "decimal(18,2)")]
    public decimal Price { get; set; }
}

2.3 Fluent API配置

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>()
        .ToTable("Products")
        .HasKey(p => p.Id);

    modelBuilder.Entity<Product>()
        .Property(p => p.Name)
        .IsRequired()
        .HasMaxLength(100);

    modelBuilder.Entity<Product>()
        .HasOne(p => p.Category)
        .WithMany(c => c.Products)
        .HasForeignKey(p => p.CategoryId);
}

三、基本CRUD操作

3.1 创建数据

using (var context = new AppDbContext())
{
    var category = new Category { Name = "电子产品" };
    var product = new Product 
    { 
        Name = "智能手机", 
        Price = 2999.99m,
        Category = category
    };

    context.Products.Add(product);
    int recordsAffected = context.SaveChanges();
    Console.WriteLine($"影响了{recordsAffected}条记录");
}

3.2 查询数据

基本查询:

// 获取所有产品
var products = context.Products.ToList();

// 条件查询
var expensiveProducts = context.Products
                             .Where(p => p.Price > 1000)
                             .OrderByDescending(p => p.Price)
                             .ToList();

包含导航属性:

var productsWithCategory = context.Products
                                .Include(p => p.Category)
                                .ToList();

3.3 更新数据

var product = context.Products.FirstOrDefault(p => p.Id == 1);
if (product != null)
{
    product.Price = 2599.99m;
    context.SaveChanges();
}

3.4 删除数据

var product = context.Products.Find(1);
if (product != null)
{
    context.Products.Remove(product);
    context.SaveChanges();
}

四、高级查询技术

4.1 复杂LINQ查询

var result = from p in context.Products
             join c in context.Categories on p.CategoryId equals c.Id
             where p.Price > 500 && c.Name.Contains("电子")
             orderby p.Price descending
             select new 
             {
                 p.Name,
                 p.Price,
                 CategoryName = c.Name
             };

4.2 原生SQL查询

执行查询:

var products = context.Products
                    .FromSqlRaw("SELECT * FROM Products WHERE Price > {0}", 500)
                    .ToList();

执行存储过程:

var products = context.Products
                    .FromSqlRaw("EXEC GetExpensiveProducts @minPrice={0}", 1000)
                    .ToList();

4.3 延迟加载与显式加载

延迟加载(需安装额外包):

// 安装包
Install-Package Microsoft.EntityFrameworkCore.Proxies

// 启用延迟加载
optionsBuilder.UseLazyLoadingProxies();

// 使用
var product = context.Products.First();
var categoryName = product.Category.Name; // 自动加载Category

显式加载:

var product = context.Products.First();
context.Entry(product)
       .Reference(p => p.Category)
       .Load();

五、数据库迁移

5.1 创建和应用迁移

# 安装EF Core工具(如未安装)
dotnet tool install --global dotnet-ef

# 创建迁移
dotnet ef migrations add InitialCreate

# 应用迁移
dotnet ef database update

5.2 自定义迁移

public partial class AddProductDescription : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AddColumn<string>(
            name: "Description",
            table: "Products",
            type: "nvarchar(500)",
            maxLength: 500,
            nullable: true);
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropColumn(
            name: "Description",
            table: "Products");
    }
}

六、性能优化

6.1 查询优化技巧

// 只选择需要的字段
var productNames = context.Products
                        .Select(p => p.Name)
                        .ToList();

// 使用AsNoTracking避免变更跟踪
var readOnlyProducts = context.Products
                            .AsNoTracking()
                            .ToList();

// 批量操作
context.BulkInsert(products);  // 使用Z.EntityFramework.Extensions等库

6.2 连接池配置

services.AddDbContextPool<AppDbContext>(options => 
    options.UseSqlServer(Configuration.GetConnectionString("Default")),
    poolSize: 128);  // 连接池大小

七、实际应用模式

7.1 仓储模式实现

public interface IRepository<T> where T : class
{
    IQueryable<T> GetAll();
    Task<T> GetByIdAsync(int id);
    Task AddAsync(T entity);
    Task UpdateAsync(T entity);
    Task DeleteAsync(T entity);
}

public class EfRepository<T> : IRepository<T> where T : class
{
    private readonly AppDbContext _context;

    public EfRepository(AppDbContext context)
    {
        _context = context;
    }

    public IQueryable<T> GetAll() => _context.Set<T>();

    public async Task<T> GetByIdAsync(int id) => await _context.Set<T>().FindAsync(id);

    public async Task AddAsync(T entity)
    {
        await _context.Set<T>().AddAsync(entity);
        await _context.SaveChangesAsync();
    }

    // 其他方法实现...
}

7.2 工作单元模式

public interface IUnitOfWork : IDisposable
{
    IRepository<Product> Products { get; }
    IRepository<Category> Categories { get; }
    Task<int> CommitAsync();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly AppDbContext _context;

    public UnitOfWork(AppDbContext context)
    {
        _context = context;
        Products = new EfRepository<Product>(_context);
        Categories = new EfRepository<Category>(_context);
    }

    public IRepository<Product> Products { get; }
    public IRepository<Category> Categories { get; }

    public async Task<int> CommitAsync() => await _context.SaveChangesAsync();

    public void Dispose() => _context.Dispose();
}

八、常见问题解决

8.1 并发冲突处理

乐观并发控制:

// 实体类添加并发令牌
public class Product
{
    [Timestamp]
    public byte[] RowVersion { get; set; }
}

// 处理并发异常
try
{
    await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
    var entry = ex.Entries.Single();
    var databaseValues = await entry.GetDatabaseValuesAsync();
    // 解决冲突逻辑...
}

8.2 连接泄露问题

正确使用DbContext:

// 在ASP.NET Core中依赖注入DbContext(自动管理生命周期)
services.AddDbContext<AppDbContext>(options => 
    options.UseSqlServer(Configuration.GetConnectionString("Default")));

// 在其他场景中使用using
using (var context = new AppDbContext())
{
    // 操作数据库
}

结语

Entity Framework Core作为.NET生态中的主流ORM框架,大大简化了数据访问层的开发。通过本教程,您应该已经掌握:

  1. EF Core基础配置:DbContext配置、实体映射
  2. 核心数据操作:CRUD操作、复杂查询
  3. 高级特性:迁移管理、性能优化
  4. 架构模式:仓储模式、工作单元模式

实际开发中建议:

  • 根据项目规模选择合适的架构模式
  • 注意DbContext的生命周期管理
  • 对性能敏感的操作考虑原生SQL或存储过程
  • 定期检查并优化生成的SQL查询

随着.NET平台的持续发展,EF Core也在不断进化。建议开发者关注官方文档,及时了解新特性和最佳实践。


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注