Skip to content

SQLServer ORM框架集成

Entity Framework Core

概述

Entity Framework Core (EF Core) 是微软官方的跨平台ORM框架,支持.NET Core和.NET Framework,提供了强大的对象关系映射功能。

安装与配置

安装 EF Core

使用 NuGet 包管理器安装 EF Core:

powershell
# 安装 SQL Server 提供程序
Install-Package Microsoft.EntityFrameworkCore.SqlServer

# 安装 EF Core 工具(用于迁移)
Install-Package Microsoft.EntityFrameworkCore.Tools

基本配置

  1. 创建实体类

    csharp
    public class Product
    {
        public int ProductId { get; set; }
        public string ProductName { get; set; }
        public decimal Price { get; set; }
        public int CategoryId { get; set; }
        public Category Category { get; set; }
    }
    
    public class Category
    {
        public int CategoryId { get; set; }
        public string CategoryName { get; set; }
        public ICollection<Product> Products { get; set; }
    }
  2. 创建数据上下文

    csharp
    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=YourServer;Database=YourDatabase;User Id=YourUser;Password=YourPassword;");
        }
        
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // 配置实体关系
            modelBuilder.Entity<Product>()
                .HasOne(p => p.Category)
                .WithMany(c => c.Products)
                .HasForeignKey(p => p.CategoryId);
        }
    }

实体映射

数据注解

csharp
public class Product
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ProductId { get; set; }
    
    [Required]
    [MaxLength(100)]
    public string ProductName { get; set; }
    
    [Column(TypeName = "decimal(18,2)")]
    public decimal Price { get; set; }
    
    public int CategoryId { get; set; }
    
    [ForeignKey("CategoryId")]
    public Category Category { get; set; }
}

Fluent API

csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>()
        .Property(p => p.ProductName)
        .IsRequired()
        .HasMaxLength(100);
        
    modelBuilder.Entity<Product>()
        .Property(p => p.Price)
        .HasColumnType("decimal(18,2)");
        
    modelBuilder.Entity<Product>()
        .HasOne(p => p.Category)
        .WithMany(c => c.Products)
        .HasForeignKey(p => p.CategoryId)
        .OnDelete(DeleteBehavior.Cascade);
}

查询操作

基本查询

csharp
using (var context = new AppDbContext())
{
    // 查询所有产品
    var products = context.Products.ToList();
    
    // 条件查询
    var expensiveProducts = context.Products
        .Where(p => p.Price > 100)
        .ToList();
    
    // 排序查询
    var sortedProducts = context.Products
        .OrderByDescending(p => p.Price)
        .ToList();
    
    // 分页查询
    var pagedProducts = context.Products
        .Skip(10)
        .Take(5)
        .ToList();
}

关联查询

csharp
using (var context = new AppDbContext())
{
    // 立即加载关联数据(Eager Loading)
    var productsWithCategories = context.Products
        .Include(p => p.Category)
        .ToList();
    
    // 延迟加载关联数据(Lazy Loading)
    // 需要安装 Microsoft.EntityFrameworkCore.Proxies 包并配置
    optionsBuilder.UseLazyLoadingProxies();
    
    var product = context.Products.First();
    // 访问关联数据时自动加载
    var categoryName = product.Category.CategoryName;
    
    // 显式加载关联数据(Explicit Loading)
    var product = context.Products.First();
    context.Entry(product).Reference(p => p.Category).Load();
}

变更跟踪

csharp
using (var context = new AppDbContext())
{
    // 添加新实体
    var product = new Product { ProductName = "New Product", Price = 50 };
    context.Products.Add(product);
    
    // 修改实体
    var existingProduct = context.Products.First();
    existingProduct.Price = 60;
    
    // 删除实体
    var productToDelete = context.Products.First(p => p.ProductId == 1);
    context.Products.Remove(productToDelete);
    
    // 保存所有变更
    context.SaveChanges();
}

迁移管理

创建迁移

powershell
# 在 Package Manager Console 中
Add-Migration InitialCreate

# 或使用 .NET CLI
dotnet ef migrations add InitialCreate

应用迁移

powershell
# 在 Package Manager Console 中
Update-Database

# 或使用 .NET CLI
dotnet ef database update

回滚迁移

powershell
# 回滚到指定迁移
Update-Database -Migration PreviousMigrationName

# 或使用 .NET CLI
dotnet ef database update PreviousMigrationName

EF Core 最佳实践

  • 合理使用加载策略(立即加载、延迟加载、显式加载)
  • 避免在循环中执行查询
  • 使用异步方法提高并发性能
  • 配置适当的索引
  • 定期清理未使用的迁移
  • 使用批量操作处理大量数据

Entity Framework 6

概述

Entity Framework 6 (EF6) 是传统的.NET Framework ORM框架,适用于.NET Framework应用程序,提供了成熟的功能集。

安装与配置

安装 EF6

powershell
Install-Package EntityFramework

配置连接字符串

App.configWeb.config 中配置连接字符串:

xml
<connectionStrings>
    <add name="AppDbContext" connectionString="Server=YourServer;Database=YourDatabase;User Id=YourUser;Password=YourPassword;" providerName="System.Data.SqlClient" />
</connectionStrings>

创建数据上下文

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

实体映射

csharp
// 数据注解与 EF Core 类似
// Fluent API 略有不同
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>()
        .Property(p => p.ProductName)
        .IsRequired()
        .HasMaxLength(100);
        
    modelBuilder.Entity<Product>()
        .HasRequired(p => p.Category)
        .WithMany(c => c.Products)
        .HasForeignKey(p => p.CategoryId);
}

查询操作

csharp
using (var context = new AppDbContext())
{
    // 基本查询
    var products = context.Products.ToList();
    
    // 立即加载
    var productsWithCategories = context.Products
        .Include(p => p.Category)
        .ToList();
    
    // 延迟加载(默认启用)
    var product = context.Products.First();
    var categoryName = product.Category.CategoryName;
}

性能优化

  • 启用缓存:EF6 内置查询缓存
  • 使用编译查询提高重复查询性能
  • 优化实体映射,避免不必要的关联
  • 使用 AsNoTracking() 提高查询性能(适用于只读查询)

EF6 vs EF Core

特性EF6EF Core
平台支持.NET Framework.NET Core/.NET Framework
跨平台
性能一般较好
迁移管理支持支持
批量操作有限支持支持
查询类型不支持支持
值对象不支持支持

Dapper

概述

Dapper 是一个轻量级ORM框架,由Stack Overflow开发,提供了高性能的数据访问能力,适合需要直接SQL控制的场景。

安装与配置

安装 Dapper

powershell
Install-Package Dapper

基本配置

csharp
// 创建数据库连接
using (var connection = new SqlConnection("Server=YourServer;Database=YourDatabase;User Id=YourUser;Password=YourPassword;"))
{
    connection.Open();
    // 使用 Dapper 执行查询
}

基本查询

查询单条记录

csharp
using (var connection = new SqlConnection(connectionString))
{
    connection.Open();
    int productId = 1;
    var product = connection.QuerySingleOrDefault<Product>("SELECT * FROM Products WHERE ProductId = @Id", new { Id = productId });
}

查询多条记录

csharp
using (var connection = new SqlConnection(connectionString))
{
    connection.Open();
    var products = connection.Query<Product>("SELECT * FROM Products WHERE Price > @MinPrice", new { MinPrice = 100 });
}

关联查询

csharp
using (var connection = new SqlConnection(connectionString))
{
    connection.Open();
    var sql = @"SELECT p.*, c.* 
               FROM Products p 
               INNER JOIN Categories c ON p.CategoryId = c.CategoryId 
               WHERE p.Price > @MinPrice";
    
    var productDictionary = new Dictionary<int, Product>();
    
    var products = connection.Query<Product, Category, Product>(
        sql,
        (product, category) =>
        {
            Product productEntry;
            if (!productDictionary.TryGetValue(product.ProductId, out productEntry))
            {
                productEntry = product;
                productEntry.Category = category;
                productDictionary.Add(productEntry.ProductId, productEntry);
            }
            return productEntry;
        },
        new { MinPrice = 100 },
        splitOn: "CategoryId"
    ).Distinct().ToList();
}

事务处理

csharp
using (var connection = new SqlConnection(connectionString))
{
    connection.Open();
    using (var transaction = connection.BeginTransaction())
    {
        try
        {
            // 执行第一个操作
            connection.Execute("UPDATE Products SET Price = @Price WHERE ProductId = @Id", 
                new { Price = 150, Id = 1 }, transaction);
            
            // 执行第二个操作
            connection.Execute("INSERT INTO OrderDetails (ProductId, Quantity) VALUES (@ProductId, @Quantity)", 
                new { ProductId = 1, Quantity = 5 }, transaction);
            
            // 提交事务
            transaction.Commit();
        }
        catch (Exception ex)
        {
            // 回滚事务
            transaction.Rollback();
            throw;
        }
    }
}

存储过程调用

csharp
using (var connection = new SqlConnection(connectionString))
{
    connection.Open();
    
    // 调用存储过程
    var products = connection.Query<Product>("GetProductsByCategory", 
        new { CategoryId = 1 }, 
        commandType: CommandType.StoredProcedure);
    
    // 调用带输出参数的存储过程
    var parameters = new DynamicParameters();
    parameters.Add("@CategoryId", 1);
    parameters.Add("@ProductCount", dbType: DbType.Int32, direction: ParameterDirection.Output);
    
    var products = connection.Query<Product>("GetProductsByCategoryWithCount", 
        parameters, 
        commandType: CommandType.StoredProcedure);
    
    int productCount = parameters.Get<int>("@ProductCount");
}

性能优化

  • 使用参数化查询避免SQL注入和提高性能
  • 使用 AsList() 代替 ToList() 提高查询性能
  • 合理使用连接池
  • 避免在循环中执行查询
  • 使用批量操作处理大量数据

Dapper 最佳实践

  • 保持SQL查询简洁高效
  • 使用参数化查询
  • 合理使用事务
  • 关闭不必要的连接
  • 使用异步方法提高并发性能

NHibernate

概述

NHibernate 是一个成熟的ORM框架,基于Java的Hibernate,提供了丰富的功能和灵活的配置选项。

安装与配置

安装 NHibernate

powershell
Install-Package NHibernate
Install-Package NHibernate.SqlServer

配置文件

创建 hibernate.cfg.xml 配置文件:

xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
  <session-factory>
    <property name="connection.connection_string">Server=YourServer;Database=YourDatabase;User Id=YourUser;Password=YourPassword;</property>
    <property name="dialect">NHibernate.Dialect.MsSql2012Dialect</property>
    <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
    <property name="show_sql">true</property>
    <property name="format_sql">true</property>
    <mapping assembly="YourAssembly" />
  </session-factory>
</hibernate-configuration>

实体映射

XML映射

创建 Product.hbm.xml 映射文件:

xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="YourAssembly" namespace="YourNamespace">
  <class name="Product" table="Products">
    <id name="ProductId" column="ProductId">
      <generator class="identity" />
    </id>
    <property name="ProductName" column="ProductName" not-null="true" length="100" />
    <property name="Price" column="Price" type="decimal(18,2)" />
    <many-to-one name="Category" column="CategoryId" class="Category" fetch="join" />
  </class>
</hibernate-mapping>

Fluent映射

csharp
public class ProductMap : ClassMapping<Product>
{
    public ProductMap()
    {
        Table("Products");
        Id(x => x.ProductId, m => m.Generator(Generators.Identity));
        Property(x => x.ProductName, m => { m.NotNullable(true); m.Length(100); });
        Property(x => x.Price, m => m.Type(NHibernateUtil.Decimal));
        ManyToOne(x => x.Category, m => m.Column("CategoryId"));
    }
}

会话管理

csharp
// 初始化会话工厂
var configuration = new Configuration();
configuration.Configure(); // 加载 hibernate.cfg.xml
var sessionFactory = configuration.BuildSessionFactory();

// 打开会话
using (var session = sessionFactory.OpenSession())
{
    using (var transaction = session.BeginTransaction())
    {
        // 执行数据库操作
        transaction.Commit();
    }
}

查询操作

HQL查询

csharp
using (var session = sessionFactory.OpenSession())
{
    var hql = "FROM Product p WHERE p.Price > :price ORDER BY p.ProductName";
    var products = session.CreateQuery(hql)
        .SetParameter("price", 100)
        .List<Product>();
}

Criteria查询

csharp
using (var session = sessionFactory.OpenSession())
{
    var criteria = session.CreateCriteria<Product>();
    criteria.Add(Restrictions.Gt("Price", 100));
    criteria.AddOrder(Order.Asc("ProductName"));
    var products = criteria.List<Product>();
}

LINQ查询

csharp
using (var session = sessionFactory.OpenSession())
{
    var products = session.Query<Product>()
        .Where(p => p.Price > 100)
        .OrderBy(p => p.ProductName)
        .ToList();
}

缓存配置

一级缓存

  • NHibernate 默认启用一级缓存(会话级缓存)
  • 同一个会话中重复查询相同数据会从缓存获取

二级缓存

  • 配置二级缓存需要额外的缓存提供程序(如 Ehcache、Redis)
  • 适合频繁访问且变化较少的数据

其他ORM框架

LINQ to SQL

  • 微软早期的ORM框架,仅支持SQL Server
  • 适用于小型应用程序
  • 已被EF替代,不再积极维护

LLBLGen Pro

  • 商业ORM框架,支持多种数据库
  • 提供可视化设计器
  • 适合大型企业应用程序

ServiceStack.OrmLite

  • 轻量级ORM框架,提供了类似Dapper的性能
  • 支持多种数据库
  • 适合ServiceStack应用程序

ORM框架比较

性能比较

框架性能特点
Dapper优秀轻量级,直接SQL控制
EF Core较好平衡性能和开发效率
NHibernate一般功能丰富,配置复杂
EF6一般成熟稳定,性能一般

功能比较

框架迁移支持批量操作缓存跨平台
Dapper支持无内置
EF Core支持支持查询缓存
NHibernate支持支持一级/二级缓存
EF6支持有限支持查询缓存

适用场景

  • Dapper:高性能要求、复杂SQL查询、存储过程频繁使用
  • EF Core:跨平台应用、需要ORM便利性、中大型应用
  • NHibernate:复杂数据模型、需要高级ORM功能、企业级应用
  • EF6:现有.NET Framework应用、需要成熟稳定的ORM

性能优化

加载策略优化

立即加载(Eager Loading)

  • 适用于关联数据经常被访问的场景
  • 减少数据库查询次数
  • 避免N+1查询问题

延迟加载(Lazy Loading)

  • 适用于关联数据不经常被访问的场景
  • 提高初始查询性能
  • 注意避免N+1查询问题

显式加载(Explicit Loading)

  • 适用于条件性访问关联数据的场景
  • 提供最精细的控制
  • 避免不必要的关联数据加载

查询优化

使用索引

  • 为经常查询的列创建索引
  • 避免在索引列上使用函数
  • 定期更新统计信息

优化查询语句

  • 只查询需要的列
  • 避免使用SELECT *
  • 使用分页减少数据传输
  • 合理使用JOIN,避免不必要的关联

使用异步方法

  • 提高并发性能
  • 减少线程阻塞
  • 适合Web应用程序

批量操作

EF Core 批量操作

csharp
// 使用EF Core扩展包进行批量操作
Install-Package EFCore.BulkExtensions

using (var context = new AppDbContext())
{
    // 批量插入
    context.BulkInsert(products);
    
    // 批量更新
    context.BulkUpdate(products);
    
    // 批量删除
    context.BulkDelete(products);
}

Dapper 批量操作

csharp
using (var connection = new SqlConnection(connectionString))
{
    connection.Open();
    
    // 使用SqlBulkCopy进行批量插入
    using (var bulkCopy = new SqlBulkCopy(connection))
    {
        bulkCopy.DestinationTableName = "Products";
        bulkCopy.WriteToServer(dataTable);
    }
}

缓存策略

查询缓存

  • EF Core 和 EF6 内置查询缓存
  • 适合频繁执行的相同查询

应用程序缓存

  • 使用Redis或MemoryCache缓存频繁访问的数据
  • 适合不变或很少变化的数据

二级缓存

  • NHibernate 内置二级缓存
  • 适合跨会话共享的数据

事务管理

ORM中的事务处理

EF Core 事务

csharp
using (var context = new AppDbContext())
using (var transaction = context.Database.BeginTransaction())
{
    try
    {
        // 执行数据库操作
        context.SaveChanges();
        
        // 执行其他数据库操作
        context.SaveChanges();
        
        transaction.Commit();
    }
    catch (Exception ex)
    {
        transaction.Rollback();
        throw;
    }
}

Dapper 事务

csharp
using (var connection = new SqlConnection(connectionString))
{
    connection.Open();
    using (var transaction = connection.BeginTransaction())
    {
        try
        {
            // 执行数据库操作
            connection.Execute("UPDATE Products SET Price = @Price WHERE ProductId = @Id", 
                new { Price = 150, Id = 1 }, transaction);
            
            transaction.Commit();
        }
        catch (Exception ex)
        {
            transaction.Rollback();
            throw;
        }
    }
}

分布式事务

  • 使用 TransactionScope 处理跨数据库或跨服务的事务
  • 注意性能影响
  • 适合关键业务操作
csharp
using (var scope = new TransactionScope())
{
    // 执行第一个数据库操作
    // 执行第二个数据库操作(不同数据库或服务)
    
    scope.Complete();
}

版本控制与迁移

数据库迁移

EF Core 迁移

  • 使用EF Core迁移工具管理数据库架构变更
  • 支持向上迁移和向下迁移
  • 适合开发和生产环境

第三方迁移工具

  • 使用Flyway或Liquibase管理数据库迁移
  • 支持多种数据库
  • 适合复杂的数据库架构管理

CI/CD 集成

在CI/CD管道中应用迁移

yaml
# Azure DevOps Pipeline 示例
- task: DotNetCoreCLI@2
  inputs:
    command: 'ef'
    arguments: 'database update --connection "Server=YourServer;Database=YourDatabase;User Id=YourUser;Password=YourPassword;"'
    workingDirectory: '$(Build.SourcesDirectory)/YourProject'

数据库部署最佳实践

  • 在开发环境测试迁移
  • 使用脚本部署数据库变更
  • 备份数据库前应用迁移
  • 监控迁移执行情况

最佳实践

ORM框架选择

  • 根据项目规模和需求选择合适的ORM框架
  • 考虑团队技能和经验
  • 评估性能要求
  • 考虑长期维护成本

团队协作

  • 建立统一的实体映射规范
  • 文档化数据库架构变更
  • 使用版本控制管理迁移脚本
  • 定期审查数据库设计

性能优化

  • 定期监控数据库性能
  • 使用查询分析器优化慢查询
  • 合理使用缓存
  • 优化数据模型设计

版本差异

SQL Server 2008/2008 R2

  • 支持EF6、NHibernate、Dapper
  • 有限支持EF Core(需要额外配置)
  • 不支持某些高级EF Core功能

SQL Server 2012

  • 支持EF6、EF Core、NHibernate、Dapper
  • 支持大部分EF Core功能
  • 支持列存储索引,提高大数据查询性能

SQL Server 2014

  • 全面支持EF Core
  • 支持内存优化表,适合高性能应用
  • 支持新的SQL功能,如窗口函数增强

SQL Server 2016

  • 支持所有现代ORM框架
  • 支持JSON数据类型,EF Core可直接映射
  • 支持行级安全性,可与ORM结合使用

SQL Server 2017

  • 支持Linux平台,ORM框架跨平台支持更好
  • 支持图形数据库,EF Core可映射图形数据
  • 性能优化,提高ORM查询效率

SQL Server 2019

  • 增强了JSON支持,EF Core可更好地处理JSON数据
  • 支持Big Data Clusters,ORM可用于大数据场景
  • 性能进一步优化

SQL Server 2022

  • 支持Ledger,ORM可与Ledger表结合使用
  • 增强了安全性功能
  • 性能持续优化

常见问题 (FAQ)

Entity Framework Core 和 Entity Framework 6 有什么区别?

EF Core 是新一代跨平台ORM框架,支持.NET Core和.NET Framework,具有更好的性能和现代化功能,如值对象支持、查询类型、批量操作等。EF6是传统的.NET Framework ORM框架,适合现有.NET Framework应用,功能成熟但性能一般。

Dapper 和 Entity Framework 哪个性能更好?

Dapper 性能通常比Entity Framework更好,因为它是轻量级ORM,直接执行SQL查询,减少了ORM的额外开销。适合需要高性能和直接SQL控制的场景。Entity Framework 提供了更好的开发效率和ORM便利性,适合中大型应用。

如何选择适合自己的ORM框架?

选择ORM框架应考虑:1) 项目规模和需求;2) 性能要求;3) 团队技能和经验;4) 平台支持;5) 长期维护成本。小型高性能应用适合Dapper,中大型应用适合EF Core,复杂企业应用适合NHibernate。

如何优化ORM框架的性能?

优化ORM性能的方法:1) 合理使用加载策略(立即加载、延迟加载、显式加载);2) 优化查询语句,避免SELECT *;3) 使用索引;4) 合理使用缓存;5) 避免N+1查询问题;6) 使用异步方法提高并发性能;7) 批量操作处理大量数据。

如何处理ORM框架中的复杂查询?

处理复杂查询的方法:1) 使用原生SQL查询(EF Core的FromSql、Dapper的直接SQL);2) 使用存储过程;3) 优化实体映射,避免不必要的关联;4) 分拆复杂查询为多个简单查询;5) 使用查询分析器优化查询性能。

如何避免ORM框架中的N+1查询问题?

避免N+1查询问题的方法:1) 使用立即加载(Include)预加载关联数据;2) 合理设计数据模型,避免过度关联;3) 使用显式加载控制关联数据加载时机;4) 使用投影查询,只查询需要的字段和关联数据。

如何管理ORM框架中的数据库迁移?

管理数据库迁移的方法:1) 使用EF Core或EF6的迁移工具;2) 使用第三方迁移工具如Flyway或Liquibase;3) 在CI/CD管道中自动化应用迁移;4) 定期备份数据库前应用迁移;5) 测试迁移在开发环境的执行情况。

ORM框架如何处理事务?

ORM框架处理事务的方式:1) EF Core和EF6使用DbContext或Database.BeginTransaction()创建事务;2) Dapper使用SqlConnection.BeginTransaction()创建事务;3) NHibernate使用Session.BeginTransaction()创建事务;4) 对于分布式事务,使用TransactionScope处理。

如何在ORM框架中使用存储过程?

在ORM框架中使用存储过程的方法:1) EF Core使用FromSql或ExecuteSqlRaw调用存储过程;2) Dapper直接调用存储过程,支持输入输出参数;3) NHibernate使用CreateSQLQuery或CallableStatement调用存储过程;4) EF6使用Database.ExecuteSqlCommand或SqlQuery调用存储过程。

如何提高ORM框架的并发性能?

提高ORM并发性能的方法:1) 使用异步方法;2) 合理使用连接池;3) 减少锁的持有时间;4) 使用乐观并发控制;5) 优化查询性能;6) 合理使用缓存;7) 避免长时间运行的事务。