首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> Oracle 資料庫 >> Oracle 資料庫基礎

Oracle LONG 類型字段 C# 存取實戰:System.Data.OracleClient 完整指南

     編輯:Oracle 資料庫基礎

前置準備與環境說明

  • 目標場景:.NET Framework 應用 (C#) 讀寫 Oracle LONG 列 (超過 4000 字節文本)。
  • 數據訪問技術:`System.Data.OracleClient` (Microsoft 提供,**已棄用**,.NET Core / .NET 5+ **不支持**),僅限 .NET Framework 4.8 及以下維護項目。
  • 現代替代**:新項目**強制使用** `Oracle.ManagedDataAccess.Core` (官方 ODP.NET Managed, 支持 .NET Core/5+/6+/7+/8+) 或 `Oracle.ManagedDataAccess` (傳統 ODP.NET)。本文代碼僅供存量系統維護參考。
  • 測試表結構:
    CREATE TABLE test_long (
        agreement_id  CHAR(3) NOT NULL PRIMARY KEY,
        agreement_content LONG NULL
    );
    

核心類與關鍵屬性

類/屬性用途LONG 特殊處理
OracleConnection連接對象連接字符串需 `Persist Security Info=True`
OracleCommand命令對象`CommandType = CommandType.Text`
OracleParameter參數對象**關鍵**:`OracleType = OracleType.LongVarChar` (或 `LongRaw`),`Direction = ParameterDirection.Input/Output`
OracleDataAdapter數據適配器`Fill(DataSet)` 自動將 LONG 映射為 `System.String` (受 .NET 字符串 2GB 限制)
OracleDataReader只讀流`GetString(ordinal)` / `GetChars(ordinal, ...)` 流式讀取大文本

完整代碼實現:插入與讀取 LONG 數據

1. 連接字符串最佳實踐

// app.config / web.config 

  


// C# 獲取
string connStr = ConfigurationManager.ConnectionStrings["OracleConn"].ConnectionString;

2. 插入 LONG 數據 (參數化防注入、流式寫入)

using System;
using System.Data;
using System.Data.OracleClient;
using System.Configuration;

public class LongDataAccess
{
    private readonly string _connStr;

    public LongDataAccess(string connStrName = "OracleConn")
    {
        _connStr = ConfigurationManager.ConnectionStrings[connStrName].ConnectionString;
    }

    /// 
    /// 插入/更新 LONG 文本內容
    /// 
    public void SaveAgreement(string agreementId, string content)
    {
        const string sql = @"
            MERGE INTO test_long t
            USING (SELECT :id AS agreement_id FROM dual) s
            ON (t.agreement_id = s.agreement_id)
            WHEN MATCHED THEN UPDATE SET t.agreement_content = :content
            WHEN NOT MATCHED THEN INSERT (agreement_id, agreement_content) VALUES (:id, :content)";

        using (var conn = new OracleConnection(_connStr))
        using (var cmd = new OracleCommand(sql, conn))
        {
            // 關鍵:參數類型必須為 OracleType.LongVarChar
            cmd.Parameters.Add(":id", OracleType.Char, 3).Value = agreementId;
            cmd.Parameters.Add(":content", OracleType.LongVarChar).Value = content ?? (object)DBNull.Value;
            // 長文本建議顯式設置 Size (可選,LongVarChar 默認無大小限制)
            // cmd.Parameters[":content"].Size = -1; // -1 表示無限制

            conn.Open();
            int rows = cmd.ExecuteNonQuery();
            Console.WriteLine($"Upsert rows: {rows}");
        }
    }
}

3. 讀取 LONG 數據 (DataAdapter 填充 DataSet / DataReader 流式讀取)

/// 
/// 方式一:DataAdapter 填充 DataSet (適合中小文本,全量載入內存)
/// 
public string GetAgreementByDataSet(string agreementId)
{
    const string sql = "SELECT agreement_content FROM test_long WHERE agreement_id = :id";
    using (var conn = new OracleConnection(_connStr))
    using (var da = new OracleDataAdapter(sql, conn))
    {
        da.SelectCommand.Parameters.Add(":id", OracleType.Char, 3).Value = agreementId;
        var ds = new DataSet();
        da.Fill(ds); // 自動將 LONG 列映射為 DataColumn (Type=System.String)
        if (ds.Tables[0].Rows.Count > 0)
            return ds.Tables[0].Rows[0]["agreement_content"] as string;
        return null;
    }
}

/// 
/// 方式二:OracleDataReader 流式讀取 (適合超大文本,避免內存壓力)
/// 
public string GetAgreementByReader(string agreementId)
{
    const string sql = "SELECT agreement_content FROM test_long WHERE agreement_id = :id";
    using (var conn = new OracleConnection(_connStr))
    using (var cmd = new OracleCommand(sql, conn))
    {
        cmd.Parameters.Add(":id", OracleType.Char, 3).Value = agreementId;
        conn.Open();
        using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) // 關鍵:SequentialAccess 允許流式讀取 LOB/LONG
        {
            if (reader.Read())
            {
                // GetString 直接返回完整字符串 (受 2GB 限制)
                return reader.GetString(reader.GetOrdinal("agreement_content"));
                
                // 超大文本分塊讀取示例 (GetChars)
                /*
                const int bufferSize = 8192;
                char[] buffer = new char[bufferSize];
                long fieldOffset = 0;
                int charsRead;
                using (var writer = new System.IO.StringWriter())
                {
                    while ((charsRead = reader.GetChars(reader.GetOrdinal("agreement_content"), fieldOffset, buffer, 0, bufferSize)) > 0)
                    {
                        writer.Write(buffer, 0, charsRead);
                        fieldOffset += charsRead;
                    }
                    return writer.ToString();
                }
                */
            }
            return null;
        }
    }
}

4. 完整測試程序

class Program
{
    static void Main(string[] args)
    {
        var dao = new LongDataAccess();
        string testId = "A01";
        string longText = new string('X', 50000); // 50KB 測試文本

        Console.WriteLine("=== Insert/Update ===");
        dao.SaveAgreement(testId, longText);

        Console.WriteLine("\n=== Read via DataSet ===");
        string result1 = dao.GetAgreementByDataSet(testId);
        Console.WriteLine($"Length: {result1?.Length ?? 0}, Match: {result1 == longText}");

        Console.WriteLine("\n=== Read via DataReader ===");
        string result2 = dao.GetAgreementByReader(testId);
        Console.WriteLine($"Length: {result2?.Length ?? 0}, Match: {result2 == longText}");

        Console.WriteLine("\nPress any key to exit...");
        Console.ReadKey();
    }
}

常見錯誤與排查

  1. ORA-01461: can bind a LONG value only for insert into a LONG column → 參數 `OracleType` 未設置為 `LongVarChar` (或 `LongRaw`),檢查 `cmd.Parameters.Add` 類型。
  2. ORA-01704: string literal too long → 直接在 SQL 文本中拼接超長字面量 (超過 4000 字符),**必須使用參數化綁定**。
  3. System.OutOfMemoryException → DataSet/DataReader `GetString` 將超大 LONG (>100MB) 全量載入托管堆。改用 `CommandBehavior.SequentialAccess` + `GetChars` 分塊流式處理,或遷移至 CLOB 使用 `OracleLob` 流。
  4. 連接池耗盡 / ORA-12519 → 未正確 `using` 釋放連接,或連接字符串 `Pooling=false` 導致頻繁建連。檢查 `Min/Max Pool Size` 配置。
  5. 編譯警告: 'OracleConnection' is obsolete → `System.Data.OracleClient` 已棄用。新項目**立即遷移**至 `Oracle.ManagedDataAccess.Core` (NuGet: `Oracle.ManagedDataAccess.Core`),命名空間改為 `Oracle.ManagedDataAccess.Client`,API 基本兼容,`OracleType.LongVarChar` 對應 `OracleDbType.LongRaw` / `Clob`。

遷移指南:從 System.Data.OracleClient 到 ODP.NET Managed (核心差異)

項目System.Data.OracleClient (舊)Oracle.ManagedDataAccess.Client (新)
命名空間`System.Data.OracleClient``Oracle.ManagedDataAccess.Client`
NuGet 包內置 .NET Framework (無包)`Oracle.ManagedDataAccess.Core` (跨平台)
參數類型枚舉`OracleType.LongVarChar``OracleDbType.Clob` / `OracleDbType.LongRaw`
LONG 讀取`GetString` / `GetChars``GetString` / `GetChars` / `OracleClob` 對象 (推薦)
CLOB 寫入不支持 (需先遷移表)`OracleClob` 流式寫入、臨時 LOB、SecureFile 支持
.NET Core 支持❌✅ (Windows/Linux/macOS)
配置文件```` section

總結

  • LONG 類型在 `System.Data.OracleClient` 中通過 `OracleType.LongVarChar` 參數實現參數化讀寫,`DataAdapter.Fill` 簡單易用,`DataReader.SequentialAccess` 適合大對象流式處理。
  • **嚴禁** SQL 拼接長文本,**必須** 參數化綁定防注入且繞過 4000 字符限制。
  • `System.Data.OracleClient` **已棄用**,**新項目禁止使用**,存量系統應規劃遷移至 `Oracle.ManagedDataAccess.Core`,同時**將數據庫 LONG 列遷移至 CLOB**,獲得完整功能與性能。
  • 連接池配置 (`Pooling=true, Min/Max Pool Size`) 對高併發應用至關重要,務必 `using` 正確釋放資源。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved