[ASP.NET MVC 小牛之路]06 - 使用 Entity Framework 完全指南

数据驱动应用开发的核心利器:掌握Entity Framework,让ASP.NET MVC的数据操作优雅高效

目录#


Entity Framework概述#

Entity Framework(EF)是微软推出的对象关系映射(ORM)框架,作为.NET生态系统中的数据访问核心技术。在ASP.NET MVC中使用EF有诸多优势:

  • 数据库无关性:支持SQL Server、MySQL、SQLite等多种数据库
  • 强类型LINQ查询:编译时检查取代SQL字符串拼接
  • 变更跟踪:自动管理实体状态变化
  • 开发效率:减少70%以上的数据访问层代码量

最新稳定版Entity Framework 6.x(支持.NET Framework)和Entity Framework Core(跨平台)都是MVC开发的理想选择

graph LR
    A[ASP.NET MVC Controller] --> B[Entity Framework]
    B --> C[Database]
    B --> D[SQL Server]
    B --> E[MySQL]
    B --> F[PostgreSQL]

Entity Framework开发模式#

1. Database First#

适用场景:已有数据库结构,需要快速生成模型

// 自动生成的实体类
[Table("Customers")]
public partial class Customer
{
    [Key]
    public int CustomerID { get; set; }
    
    [Required]
    [StringLength(50)]
    public string Name { get; set; }
}

2. Model First#

在设计器中创建EDMX模型,同步生成数据库和代码

3. Code First(推荐)#

现代开发的首选方式,通过C#类定义模型:

public class Product
{
    public int ProductID { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public DateTime CreatedDate { get; set; } = DateTime.Now;
}

项目集成实战#

步骤1:安装NuGet包#

Install-Package EntityFramework

步骤2:创建DbContext#

public class AppDbContext : DbContext
{
    public AppDbContext() : base("name=DefaultConnection") { }
    
    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }
    
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Fluent API配置
        modelBuilder.Entity<Product>()
            .HasRequired(p => p.Category)
            .WithMany(c => c.Products)
            .HasForeignKey(p => p.CategoryId);
    }
}

步骤3:配置连接字符串#

Web.config配置:

<connectionStrings>
  <add name="DefaultConnection" 
       connectionString="Server=.;Database=MvcDemo;User Id=sa;Password=123;"
       providerName="System.Data.SqlClient"/>
</connectionStrings>

CRUD操作指南#

创建记录#

using(var db = new AppDbContext())
{
    var newProduct = new Product { Name = "iPad", Price = 2999 };
    db.Products.Add(newProduct);
    db.SaveChanges(); // 返回受影响行数
}

查询记录#

基本LINQ查询:

// 获取所有价格大于1000的产品
var expensiveProducts = db.Products
                          .Where(p => p.Price > 1000)
                          .OrderByDescending(p => p.Price)
                          .ToList();

带关联查询:

var productsWithCategory = db.Products
    .Include(p => p.Category)  // 预加载关联数据
    .Select(p => new {
        p.Name,
        CategoryName = p.Category.Name
    }).ToList();

更新记录#

var product = db.Products.Find(1);
if(product != null)
{
    product.Price = 2599;
    db.SaveChanges();
}

删除记录#

var product = db.Products.FirstOrDefault(p => p.Name == "iPod");
if(product != null)
{
    db.Products.Remove(product);
    db.SaveChanges();
}

数据注解与配置#

数据注解方式#

public class User
{
    [Key]
    public int UserId { get; set; }
 
    [Required]
    [StringLength(100)]
    public string Email { get; set; }
 
    [Column("BirthDate")]
    public DateTime DateOfBirth { get; set; }
}

Fluent API(更强大的配置)#

在OnModelCreating中配置:

modelBuilder.Entity<User>()
    .Property(u => u.Email)
    .IsRequired()
    .HasMaxLength(100);
    
modelBuilder.Entity<User>()
    .Ignore(u => u.TemporaryCode); // 排除属性

迁移管理#

Code First的核心优势 - 优雅的数据库版本控制

启用迁移#

Enable-Migrations

添加迁移#

Add-Migration AddUserTable

生成的迁移文件:

public partial class AddUserTable : DbMigration
{
    public override void Up()
    {
        CreateTable("Users",
            c => new {
                UserId = c.Int(identity: true),
                Email = c.String(maxLength: 100),
                BirthDate = c.DateTime()
            })
            .PrimaryKey(t => t.UserId);
    }
    
    public override void Down()
    {
        DropTable("Users");
    }
}

更新数据库#

Update-Database

性能优化策略#

1. 禁用延迟加载#

public class AppDbContext : DbContext
{
    public AppDbContext()
    {
        this.Configuration.LazyLoadingEnabled = false;
    }
}

2. 使用异步操作#

public async Task<List<Product>> GetProductsAsync()
{
    return await db.Products.ToListAsync();
}

3. 避免N+1查询#

使用Include预加载关联数据

// 错误方式 - N+1查询问题
foreach(var category in db.Categories)
{
    foreach(var product in category.Products) // 每次循环查询数据库
    {...}
}
 
// 正确方式
var categories = db.Categories.Include(c => c.Products).ToList();

4. 批量操作优化#

使用AddRange代替循环Add

// 低效
foreach(var item in list)
{
    db.Products.Add(item);
}
 
// 高效
db.Products.AddRange(productList);

最佳实践#

  1. DbContext生命周期
    控制为Per Request(在ASP.NET MVC中推荐使用DI注入)

  2. 仓储模式应用

    public interface IProductRepository
    {
        IEnumerable<Product> GetFeaturedProducts();
    }
     
    public class ProductRepository : IProductRepository
    {
        private AppDbContext _context;
        
        public ProductRepository(AppDbContext context)
        {
            _context = context;
        }
    }
  3. 连接字符串加密

    <connectionStrings configProtectionProvider="RsaProtectedConfigurationProvider">
      <EncryptedData>...</EncryptedData>
    </connectionStrings>
  4. 敏感数据日志过滤

    dbContext.Database.Log = log => {
        if(log.Contains("Password"))
            Debug.WriteLine("-- SECRET DATA --");
        else
            Debug.WriteLine(log);
    };
  5. 分离DbContext配置

    public class ProductConfiguration : EntityTypeConfiguration<Product>
    {
        public ProductConfiguration()
        {
            Property(p => p.Name).IsRequired().HasMaxLength(200);
        }
    }
     
    // 在DbContext中注册
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new ProductConfiguration());
    }

总结#

Entity Framework在ASP.NET MVC开发中扮演着关键角色:

  1. 掌握Code First模式可提升开发效率
  2. 迁移机制实现优雅的数据库版本控制
  3. LINQ语法提供强类型查询能力
  4. 合理的性能优化策略保障应用高效运行
  5. 遵循最佳实践构建可维护的架构

关键认知转变:不再思考SQL语句编写,而是专注于业务对象操作

参考资料#

  1. 微软官方EF文档
  2. Entity Framework教程 by EntityFrameworkTutorial
  3. 《Entity Framework in Action, 2nd Edition》- Jon P Smith
  4. EF Core性能优化指南
  5. Dapper vs EF Core性能对比