技術棧演進與版本定位
| 時代 | 數據訪問技術 | DB2 驅動 | 現狀 |
| .NET Framework 1.x (VS.NET 2002/2003) | ODBC .NET Provider | IBM DB2 ODBC Driver | 已淘汰 |
| .NET Framework 2.0+ (VS 2005+) | OLE DB .NET Provider (System.Data.OleDb) | Microsoft OLE DB Provider for DB2 (Host Integration Server 組件) | 傳統維護模式 |
| .NET Core / .NET 5+ (現代) | ADO.NET Core (System.Data.Common) | IBM Data Server Provider (NetProvider) — IBM.Data.DB2.Core NuGet | 推薦首選 |
| .NET Core / .NET 5+ | Entity Framework Core | IBM.EntityFrameworkCore NuGet | ORM 首選 |
⚠️ 重要提示:本文重寫聚焦現代 .NET 6+ / .NET 8 環境下使用 IBM 官方 NetProvider (IBM.Data.DB2.Core) 的最佳實踐。原文基於 VS.NET Beta 2 / HIS 2000 的 OLE DB 方案已過時,僅供歷史維護參考。新項目請直接使用 IBM.Data.DB2.Core。
現代方案:IBM.Data.DB2.Core (NetProvider) 完整實戰
1. NuGet 依賴安裝
# Package Manager Console
Install-Package IBM.Data.DB2.Core -Version 7.0.0.100 # 或最新穩定版
Install-Package IBM.Data.DB2.Core-lnx # Linux 運行時依賴 (自動包含)
# 或 .NET CLI
dotnet add package IBM.Data.DB2.Core
2. 連接字符串標準化
// appsettings.json
{
"ConnectionStrings": {
"DB2Connection": "Server=192.168.1.100:50000;Database=SAMPLE;UID=db2inst1;PWD=StrongPass123!;Connection Timeout=30;Pooling=true;Min Pool Size=5;Max Pool Size=100;Certificate=SSL_CERT_PATH;SSL=Required;"
}
}
// 關鍵參數解析:
// Server=host:port (IPv6 用 [::1]:50000)
// Database=目標數據庫別名
// UID/PWD=認證
// Pooling=true (默認啟用,生產必開)
// Min/Max Pool Size=連接池邊界
// SSL=Required/Preferred/None (生產建議 Required)
// Certificate=客戶端證書路徑 (雙向 SSL)
3. DbContext / DbConnection 使用模式
// 模式 A: 純 ADO.NET (高性能、精細控制)
using IBM.Data.DB2.Core;
using (var conn = new DB2Connection(connectionString))
{
await conn.OpenAsync();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT EMPNO, FIRSTNME, LASTNAME, SALARY FROM HR.EMPLOYEE WHERE WORKDEPT = @dept";
cmd.Parameters.Add(new DB2Parameter("@dept", DB2Type.Char, 3) { Value = "A00" });
using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
Console.WriteLine($"{reader.GetString(0)} {reader.GetString(1)} {reader.GetString(2)} ${reader.GetDecimal(3)}");
}
}
}
}
// 模式 B: Dapper 微型 ORM (推薦輕量級)
using Dapper;
using (var conn = new DB2Connection(connectionString))
{
var employees = await conn.QueryAsync(
"SELECT EMPNO, FIRSTNME, LASTNAME, SALARY FROM HR.EMPLOYEE WHERE WORKDEPT = @dept",
new { dept = "A00" });
}
// 模式 C: Entity Framework Core (領域模型、變更追蹤)
public class SampleContext : DbContext
{
public DbSet Employees { get; set; }
public DbSet Departments { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseDB2(connectionString); // 需 IBM.EntityFrameworkCore
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity(e => {
e.ToTable("EMPLOYEE", "HR");
e.HasKey(x => x.Empno);
e.Property(x => x.Empno).HasColumnName("EMPNO").HasMaxLength(6).IsFixedLength();
e.Property(x => x.FirstName).HasColumnName("FIRSTNME").HasMaxLength(12);
// ... 其他映射
});
}
}
// 使用
using (var ctx = new SampleContext())
{
var emps = await ctx.Employees.Where(e => e.WorkDept == "A00").ToListAsync();
}
4. 參數化查詢與類型映射最佳實踐
| DB2 類型 | .NET 類型 | DB2Parameter 類型 | 注意事項 |
| CHAR(n) | string | DB2Type.Char | 固定長度,需 PadRight(n) 或讀取時 TrimEnd() |
| VARCHAR(n) | string | DB2Type.VarChar | 推薦新表使用 |
| GRAPHIC/VARGRAPHIC | string | DB2Type.Graphic / VarGraphic | 雙字節字符集 |
| SMALLINT | short / Int16 | DB2Type.SmallInt | |
| INTEGER | int / Int32 | DB2Type.Integer | |
| BIGINT | long / Int64 | DB2Type.BigInt | |
| DECIMAL(p,s) | decimal | DB2Type.Decimal | Precision/Scale 需匹配 |
| REAL | float / Single | DB2Type.Real | |
| DOUBLE | double | DB2Type.Double | |
| DATE | DateTime / DateOnly | DB2Type.Date | DateOnly (.NET 6+) 更語義化 |
| TIME | TimeSpan / TimeOnly | DB2Type.Time | TimeOnly (.NET 6+) 推薦 |
| TIMESTAMP | DateTime | DB2Type.Timestamp | 含微秒精度 |
| BLOB/CLOB | byte[] / string | DB2Type.Blob / Clob | 大對象建議流式讀寫 |
| XML | string / XmlDocument | DB2Type.Xml | 需 XML 列許可 |
5. 大對象 (LOB) 流式處理
// 讀取 BLOB (照片) - 流式避免大內存
using (var conn = new DB2Connection(connStr))
{
await conn.OpenAsync();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT PHOTO FROM HR.EMP_PHOTO WHERE EMPNO = @empno";
cmd.Parameters.Add(new DB2Parameter("@empno", DB2Type.Char, 6) { Value = "000010" });
using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess))
{
if (await reader.ReadAsync())
{
using (var stream = reader.GetStream(0)) // DB2DataReader.GetStream
using (var fileStream = File.Create("photo.jpg"))
{
await stream.CopyToAsync(fileStream); // 直接寫入文件,不經內存緩衝
}
}
}
}
}
// 寫入 BLOB - 參數綁定大對象
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "INSERT INTO HR.EMP_PHOTO (EMPNO, PHOTO_FORMAT, PHOTO) VALUES (@empno, @fmt, @photo)";
cmd.Parameters.Add(new DB2Parameter("@empno", DB2Type.Char, 6) { Value = "000010" });
cmd.Parameters.Add(new DB2Parameter("@fmt", DB2Type.VarChar, 10) { Value = "JPEG" });
var photoParam = new DB2Parameter("@photo", DB2Type.Blob) {
Value = File.ReadAllBytes("photo.jpg") // 小文件可直接 byte[]
// 大文件: 使用 DB2Parameter.Value = File.OpenRead("large_photo.jpg") 配合 SequentialAccess
};
cmd.Parameters.Add(photoParam);
await cmd.ExecuteNonQueryAsync();
}
6. 事務管理與隔離級別
// 顯式事務 (ADO.NET)
using (var conn = new DB2Connection(connStr))
{
await conn.OpenAsync();
using (var tx = await conn.BeginTransactionAsync(IsolationLevel.ReadCommitted)) // DB2 默認 CS
{
try
{
// ... 多條 DML
await tx.CommitAsync();
}
catch
{
await tx.RollbackAsync();
throw;
}
}
}
// EF Core 事務
using (var ctx = new SampleContext())
{
using (var tx = await ctx.Database.BeginTransactionAsync(IsolationLevel.ReadCommitted))
{
try
{
// ... 實體操作
await ctx.SaveChangesAsync();
await tx.CommitAsync();
}
catch
{
await tx.RollbackAsync();
throw;
}
}
}
歷史方案參考:OLE DB Provider (僅維護舊系統用)
前置條件:Microsoft Host Integration Server (HIS) 2016/2020
- HIS 安裝介質中包含 Microsoft OLE DB Provider for DB2
- 安裝時選擇 Data Integration 角色
- 提供
DB2OLEDB Provider (32/64 位均需)
連接字符串 (OLE DB)
Provider=DB2OLEDB;Data Source=QCDB;Network Transport Library=TCPIP;
Host CCSID=37;PC Code Page=1252;Network Address=192.168.1.100;Network Port=50000;
Package Collection=QCDB;Default Schema=USERID;User ID=userid;Password=pass;
Initial Catalog=QCDB;Persist Security Info=True;
C# 代碼 (System.Data.OleDb)
using System.Data.OleDb;
using (var conn = new OleDbConnection(oleDbConnStr))
{
await conn.OpenAsync();
using (var cmd = new OleDbCommand("SELECT * FROM ADDRESS", conn))
using (var adapter = new OleDbDataAdapter(cmd))
{
var dt = new DataTable();
adapter.Fill(dt); // 填充 DataTable
// 數據綁定到 GridView 等
}
}
性能優化與連接池調優
| 優化點 | 配置/代碼 | 效果 |
| 連接池預熱 | 啟動時創建 MinPoolSize 個連接 | 首請求零延遲 |
| 批量操作 | DB2Command.ArrayBindCount + Parameter.Value = Array | INSERT/UPDATE 吞吐提升 10-50x |
| 預取行數 | DB2Command.RowSize / FetchSize 調大 (如 1000) | 減少網絡往返 |
| 只讀查詢 | CommandBehavior.SequentialAccess + 單次讀取 | 大結果集流式處理 |
| 準備語句緩存 | DB2 自動包緩存 + 應用層重用 Command | 編譯開銷歸零 |
| 異步管道 | await OpenAsync/ExecuteReaderAsync/ExecuteNonQueryAsync | 釋放執行緒、提升併發 |
常見問題排查
🔧 典型錯誤與解決
- SQL30081N (通信錯誤):檢查防火牆、端口、HOSTNAME/IP、DB2COMM=TCPIP、svcename 配置
- SQL1042C / SQL30082N (授權/認證失敗):UID/PWD 錯誤、用戶被鎖定、AUTHENTICATION 不匹配 (SERVER vs SERVER_ENCRYPT vs DATA_ENCRYPT)
- SQL0805N (包未找到):靜態 SQL 需 BIND,動態 SQL 需 BINDADD 權限,運行
db2bind 或授權
- SQL0901N/0902C (系統錯誤):查看
db2diag.log,常見於 LOB 內存不足、臨時表空間滿、代理進程崩潰
- 中文亂碼:確保 CCSID/Code Page 一致 (DB: 1386/GBK, Client: 1252/UTF-8),連接字符串指定
CurrentSchema 避免隱式解析
- .NET Core Linux 部署:需安裝
IBM.Data.DB2.Core-lnx 運行時依賴,Dockerfile 中 RUN apt-get install -y libxml2 libaio1
遷移建議:從 OLE DB 到 NetProvider
- 將
System.Data.OleDb 替換為 IBM.Data.DB2.Core
- 連接字符串簡化為關鍵字格式 (Server/Database/UID/PWD/SSL)
- 參數類型從
OleDbType 映射到 DB2Type
- 利用
DB2Command.ArrayBindCount 實現高性能批量操作
- 引入 Dapper/EF Core 簡化數據訪問層
- 配置 SSL 加密傳輸 (生產環境必備)
總結
現代 .NET 連接 DB2 的標準路徑:IBM.Data.DB2.Core (NuGet) + 關鍵字連接字符串 + 參數化查詢 + 異步模式 + Dapper/EF Core。放棄過時的 HIS/OLE DB 架構,擁抱跨平台、高性能、雲原生的 NetProvider。核心優勢:
- 無額外中間件 (HIS),單一 NuGet 依賴
- 原生 .NET Core/.NET 5+ 支持,Linux/Windows/macOS 同樣運行
- Array Binding、流式 LOB、異步管道等高性能特性
- 活躍維護,同步 DB2 新特性 (JSON、XML、向量、時間旅行)