使用 `Microsoft.Jet.OLEDB.4.0` / `Microsoft.ACE.OLEDB.12.0` 讀取 Excel 時,欄位前幾列為數字、後續混入文字會導致文字讀為 `null`。根因:OLEDB 驅動程式預設掃描前 8 列 (`TypeGuessRows=8`) 推斷型別,數字佔多數則整欄視為數值型,不符合者捨棄為 `DBNull`。
雙重解決方案 (必須同時奏效)
1. 連接字串加入 `IMEX=1` (關鍵)
string connStr = @"Provider=Microsoft.ACE.OLEDB.12.0;
Data Source=" + filePath + @";
Extended Properties='Excel 12.0 Xml;HDR=YES;IMEX=1;'";
- HDR=YES:第一列為標題
- IMEX=1:強制混合型欄位視為文字 (Import Mixed Types),解決型別衝突導致資料遺失
- IMEX 參數值:`0`=匯出模式、`1`=匯入混合模式、`2`=匯入嚴格模式
2. 登錄檔修改 `TypeGuessRows = 0` (掃描全欄)
路徑 (視 Office 位元數與版本):
- 32 位元 Office / 64 位元系統 WoW64:
`HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Office\16.0\Access Connectivity Engine\Engines\Excel\TypeGuessRows`
- 64 位元 Office:
`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\16.0\Access Connectivity Engine\Engines\Excel\TypeGuessRows`
- 舊版 Jet 4.0:
`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Excel\TypeGuessRows`
將值從 `8` 改為 `0` (掃描所有列決定型別) 或較大數值 (如 `1000`)。
原理深度解析
- 驅動程式讀取前 N 列 (N = TypeGuessRows)
- 統計每欄型別出現次數:數值、文字、日期、布林、錯誤
- 佔多數者成為該欄「決定型別」
- 後續列資料若不符合決定型別 → 回傳 `DBNull.Value`
範例:前 8 列 5 個數字 + 3 個文字 → 決定型別 = 數值 → 第 9 列文字變 `null`
驗證步驟
- 修改登錄檔 `TypeGuessRows=0`
- 連接字串加 `IMEX=1; HDR=YES`
- 重啟 IIS / 應用程式集區 (登錄檔變更需進程重啟生效)
- 測試讀取混合型別欄位,確認文字不再為 `null`
進階:ACE OLEDB 12.0/16.0 相容 .xlsx (必要)
Jet 4.0 **不支援 .xlsx**,必須使用 ACE:
// .xlsx (Excel 2007+)
Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties='Excel 12.0 Xml;HDR=YES;IMEX=1;'
// .xls (Excel 97-2003)
Provider=Microsoft.Jet.OLEDB.4.0;Extended Properties='Excel 8.0;HDR=YES;IMEX=1;'
生產環境部署需安裝 **Microsoft Access Database Engine 2016 Redistributable** (含 ACE 16.0),並設定應用程式集區「啟用 32 位元應用程式 = True」(若用 32 位元 ACE)。
現代替代方案:免驅動、跨平台、強型別
| 庫 | 優點 | 適用場景 |
| EPPlus (商業授權) | 功能最完整、效能佳、支援公式/圖表/樣式 | 企業級報表、複雜匯出 |
| ClosedXML (MIT) | API 直覺、支援 .xlsx、免費 | 一般匯入匯出 |
| NPOI (Apache) | 支援 .xls/.xlsx、跨平台、免費 | Linux/容器環境、舊格式相容 |
| ExcelDataReader | 唯讀、串流、記憶體效率高 | 大檔案匯入、ETL |
ClosedXML 讀取混合型別範例 (無型別推斷問題)
using ClosedXML.Excel;
var workbook = new XLWorkbook(filePath);
var worksheet = workbook.Worksheet(1);
var rows = worksheet.RowsUsed().Skip(1); // 跳過標題
foreach (var row in rows)
{
// 直接取得字串,自動處理數字/日期/文字轉換
string colA = row.Cell(1).GetString();
string colB = row.Cell(2).GetString();
// 或取得型別化值
var val = row.Cell(1).Value; // XLCellValue (可隱式轉換)
}
總結
**OLEDB 方案必做兩件事**:連接字串 `IMEX=1` + 登錄檔 `TypeGuessRows=0`。新專案**強烈建議改用 ClosedXML/NPOI/EPPlus**,徹底擺脫驅動程式相依、位元數衝突、登錄檔修改、權限問題,並獲得更豐富的 API 與型別安全。
- 上一篇文章: ASP.NET 頁面間傳值完整指南:PreviousPage、QueryString、Session、Context、Cross-Page Postback 實戰比較
- 下一篇文章: ASP.NET 伺服器控制項開發核心:控制項生命週期 11 階段深度解析
Copyright ©
程式碼之家 All Rights Reserved