在現代應用開發中,數據持久化是核心需求之一。作為.NET生態系統中的主力語言,C#提供了豐富多樣的數據庫訪問技術和工具。本文將全面探討C#中的數據庫訪問方式,重點介紹三種主流ORM(對象關系映射)框架:Entity Framework Core、Dapper和NHibernate,幫助開發者根據項目需求做出合理的技術選型。
第一部分:基礎數據庫訪問 - ADO.NET
1.1 ADO.NET架構概述
ADO.NET是.NET平臺數據訪問的基石,提供了一套面向連接的、斷開式的數據訪問模型。其主要組件包括:
-
Connection:建立與數據庫的連接
-
Command:執行SQL命令
-
DataReader:高效的前向只讀數據流
-
DataAdapter:在數據源和DataSet之間架橋
-
DataSet:內存中的數據庫表示
1.2 典型使用模式
// 連接字符串示例
string connStr = "Server=.;Database=Northwind;Integrated Security=True;";using (SqlConnection conn = new SqlConnection(connStr))
{// 打開連接conn.Open();// 執行查詢string sql = "SELECT ProductID, ProductName FROM Products WHERE CategoryID = @CategoryID";using (SqlCommand cmd = new SqlCommand(sql, conn)){cmd.Parameters.AddWithValue("@CategoryID", 1);using (SqlDataReader reader = cmd.ExecuteReader()){while (reader.Read()){Console.WriteLine($"{reader["ProductID"]}: {reader["ProductName"]}");}}}// 執行插入string insertSql = "INSERT INTO Products (ProductName, CategoryID) VALUES (@Name, @CatID)";using (SqlCommand cmd = new SqlCommand(insertSql, conn)){cmd.Parameters.AddWithValue("@Name", "New Product");cmd.Parameters.AddWithValue("@CatID", 1);int rowsAffected = cmd.ExecuteNonQuery();Console.WriteLine($"插入了 {rowsAffected} 行");}
}
1.3 ADO.NET優缺點分析
優點:
-
最接近數據庫底層的訪問方式
-
性能最佳
-
完全控制SQL語句
-
適合復雜查詢和存儲過程調用
缺點:
-
需要手動編寫大量樣板代碼
-
容易產生SQL注入漏洞
-
對象關系映射需要手動實現
-
維護成本高
第二部分:現代ORM框架
2.1 Entity Framework Core
2.1.1 EF Core概述
Entity Framework Core是微軟官方推出的ORM框架,是.NET生態中最流行的數據訪問解決方案。
2.1.2 核心概念
-
DbContext:數據庫會話,工作單元模式的實現
-
DbSet:實體集合,對應數據庫表
-
遷移(Migrations):代碼優先的數據庫架構管理
-
LINQ提供程序:將LINQ轉換為SQL
2.1.3 完整示例
// 定義實體
public class Blog
{public int BlogId { get; set; }public string Url { get; set; }public int Rating { get; set; }public List<Post> Posts { get; set; }
}public class Post
{public int PostId { get; set; }public string Title { get; set; }public string Content { get; set; }public int BlogId { get; set; }public Blog Blog { get; set; }
}// 定義DbContext
public class BloggingContext : DbContext
{public DbSet<Blog> Blogs { get; set; }public DbSet<Post> Posts { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder options)=> options.UseSqlServer("Server=.;Database=BloggingDemo;Trusted_Connection=True;");
}// 使用示例
using (var db = new BloggingContext())
{// 創建var blog = new Blog { Url = "http://example.com", Rating = 5 };db.Blogs.Add(blog);db.SaveChanges();// 查詢var topBlogs = db.Blogs.Where(b => b.Rating > 3).OrderByDescending(b => b.Rating).ToList();// 更新var firstBlog = db.Blogs.First();firstBlog.Url = "http://updated.com";db.SaveChanges();// 刪除var lastBlog = db.Blogs.OrderBy(b => b.BlogId).Last();db.Blogs.Remove(lastBlog);db.SaveChanges();
}
2.1.4 EF Core高級特性
-
延遲加載:
virtual
導航屬性+代理 -
預先加載:
Include
/ThenInclude
-
顯式加載:
Entry(...).Collection(...).Load()
-
全局查詢過濾器:
modelBuilder.Entity<T>().HasQueryFilter(...)
-
影子屬性:模型中未定義的列
-
值轉換器:自定義類型映射
2.2 Dapper
2.2.1 Dapper概述
Dapper是Stack Overflow開發的高性能微型ORM,在ADO.NET基礎上提供了簡單的對象映射功能。
2.2.2 核心特點
-
輕量級(單個文件)
-
幾乎零學習成本
-
性能接近原生ADO.NET
-
支持多映射和存儲過程
2.2.3 完整示例
using Dapper;
using System.Data.SqlClient;// 基本查詢
string connectionString = "Server=.;Database=Northwind;Integrated Security=True;";using (var connection = new SqlConnection(connectionString))
{var products = connection.Query<Product>("SELECT * FROM Products WHERE CategoryID = @CatID",new { CatID = 1 });foreach (var product in products){Console.WriteLine(product.ProductName);}
}// 多映射示例
var sql = @"SELECT p.*, c.* FROM Products p INNER JOIN Categories c ON p.CategoryID = c.CategoryIDWHERE p.ProductID = @Id";using (var connection = new SqlConnection(connectionString))
{var product = connection.Query<Product, Category, Product>(sql,(prod, cat) => { prod.Category = cat; return prod; },new { Id = 1 },splitOn: "CategoryID").FirstOrDefault();Console.WriteLine($"{product.ProductName} 屬于 {product.Category.CategoryName}");
}// 執行存儲過程
using (var connection = new SqlConnection(connectionString))
{var parameters = new DynamicParameters();parameters.Add("@CategoryID", 1);parameters.Add("@ProductCount", dbType: DbType.Int32, direction: ParameterDirection.Output);var products = connection.Query<Product>("CustOrderHist",parameters,commandType: CommandType.StoredProcedure);int productCount = parameters.Get<int>("@ProductCount");
}
2.3 NHibernate
2.3.1 NHibernate概述
NHibernate是.NET平臺上的成熟ORM框架,移植自Java的Hibernate,功能全面但配置復雜。
2.3.2 核心概念
-
SessionFactory:線程安全,應用生命周期單例
-
Session:工作單元,非線程安全
-
映射文件:.hbm.xml或Fluent配置
-
HQL:Hibernate查詢語言
-
Criteria API:類型安全的查詢構建
2.3.3 完整示例
<!-- Product.hbm.xml -->
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"><class name="Product" table="Products"><id name="Id" column="ProductID"><generator class="identity" /></id><property name="Name" column="ProductName" /><property name="Price" /><many-to-one name="Category" column="CategoryID" /></class>
</hibernate-mapping>
// 配置和用法
var cfg = new Configuration();
cfg.Configure(); // 讀取hibernate.cfg.xml
cfg.AddAssembly(typeof(Product).Assembly);var sessionFactory = cfg.BuildSessionFactory();using (var session = sessionFactory.OpenSession())
using (var tx = session.BeginTransaction())
{// 保存var newProduct = new Product { Name = "New Product", Price = 9.99m,Category = session.Load<Category>(1)};session.Save(newProduct);// 查詢var products = session.CreateCriteria<Product>().Add(Restrictions.Like("Name", "N%")).List<Product>();// HQL查詢var hqlProducts = session.CreateQuery("FROM Product WHERE Price > :price").SetDecimal("price", 5m).List<Product>();tx.Commit();
}
第三部分:技術選型指南
3.1 比較矩陣
特性 | ADO.NET | Dapper | EF Core | NHibernate |
---|---|---|---|---|
學習曲線 | 高 | 低 | 中 | 高 |
性能 | 最高 | 高 | 中 | 中 |
開發速度 | 低 | 中 | 高 | 中 |
功能豐富度 | 低 | 低 | 高 | 最高 |
LINQ支持 | 無 | 有限 | 完整 | 部分 |
遷移工具 | 無 | 無 | 有 | 有 |
社區支持 | 高 | 高 | 最高 | 中 |
3.2 推薦場景
選擇ADO.NET當:
-
需要極致性能
-
項目簡單,表結構穩定
-
需要完全控制SQL
-
使用復雜存儲過程
選擇Dapper當:
-
需要接近原生性能
-
項目使用微服務架構
-
已有良好設計的SQL
-
需要輕量級解決方案
選擇EF Core當:
-
開發速度是關鍵因素
-
使用代碼優先開發
-
需要LINQ支持
-
項目中等復雜度
選擇NHibernate當:
-
需要最豐富的ORM功能
-
項目非常復雜
-
需要二級緩存等高級特性
-
團隊有Hibernate經驗
第四部分:最佳實踐與性能優化
4.1 通用最佳實踐
-
連接管理:始終確保連接被正確關閉(使用
using
語句) -
參數化查詢:永遠使用參數防止SQL注入
-
錯誤處理:實現適當的重試機制
-
日志記錄:記錄關鍵數據庫操作
-
分頁處理:大數據集必須分頁
4.2 EF Core特定優化
-
批量操作:
// 不好的做法 foreach (var item in items) {context.Add(item);context.SaveChanges(); // 每次保存 }// 好的做法 context.AddRange(items); context.SaveChanges(); // 批量保存
-
選擇合適的數據加載方式:
// 預先加載(查詢時) var blogs = context.Blogs.Include(b => b.Posts).ToList();// 顯式加載(需要時) var blog = context.Blogs.First(); context.Entry(blog).Collection(b => b.Posts).Load();// 延遲加載(訪問時,需配置) public class Blog {public virtual ICollection<Post> Posts { get; set; } }
-
禁用跟蹤:
var blogs = context.Blogs.AsNoTracking() // 只讀操作使用.ToList();
-
使用原始SQL優化復雜查詢:
var blogs = context.Blogs.FromSqlRaw("SELECT * FROM Blogs WHERE Rating > {0}", 3).ToList();
4.3 Dapper優化技巧
-
使用多映射減少查詢次數:
var sql = @"SELECT * FROM Orders o INNER JOIN Customers c ON o.CustomerID = c.CustomerIDWHERE o.OrderDate > @Date";var orders = connection.Query<Order, Customer, Order>(sql,(order, customer) => { order.Customer = customer; return order; },new { Date = DateTime.Now.AddDays(-30) },splitOn: "CustomerID");
-
批量插入:
var sql = "INSERT INTO Products (Name, Price) VALUES (@Name, @Price)"; connection.Execute(sql, products); // products是對象列表
結語
C#生態系統提供了從底層到高層的完整數據庫訪問解決方案。對于大多數應用場景,Entity Framework Core提供了最佳的生產力和功能平衡;對性能敏感的場景,Dapper是不二之選;而復雜的企業級應用可能會從NHibernate的豐富功能中受益。無論選擇哪種技術,理解底層原理和遵循最佳實踐都是構建高效、可維護數據訪問層的關鍵。
希望這篇全面指南能幫助您在C#項目中做出明智的數據庫訪問技術決策。根據項目需求、團隊技能和性能要求選擇最適合的工具,并記住:沒有放之四海而皆準的解決方案,只有最適合特定場景的選擇。
?