外观
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基本配置
创建实体类
csharppublic 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; } }创建数据上下文
csharppublic 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 PreviousMigrationNameEF Core 最佳实践
- 合理使用加载策略(立即加载、延迟加载、显式加载)
- 避免在循环中执行查询
- 使用异步方法提高并发性能
- 配置适当的索引
- 定期清理未使用的迁移
- 使用批量操作处理大量数据
Entity Framework 6
概述
Entity Framework 6 (EF6) 是传统的.NET Framework ORM框架,适用于.NET Framework应用程序,提供了成熟的功能集。
安装与配置
安装 EF6
powershell
Install-Package EntityFramework配置连接字符串
在 App.config 或 Web.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
| 特性 | EF6 | EF 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) 避免长时间运行的事务。
