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

SQL Server CLR 預存程序回傳結果集實作:以 C# 列舉資料夾檔案為例

     編輯:關於SqlServer

前言:CLR 整合將 .NET 功能帶入資料庫引擎

SQL Server 2005+ 整合 **Common Language Runtime (CLR)**,允許使用 C#/VB.NET 撰寫預存程序、觸發器、使用者定義函數/彙總/類型。典型場景:**檔案系統存取、正則表達式、複雜數學運算、外部 API 呼叫、加密/壓縮** 等 T-SQL 難以或無法高效完成的工作。本文示範建立 CLR 預存程序回傳標準結果集 (資料列/欄位)。

啟用 CLR 整合 (一次性設定)


-- 方法 1: sp_configure (需 sysadmin)
sp_configure 'clr enabled', 1;
RECONFIGURE;
GO

-- 方法 2: 外圍應用配置工具 (Surface Area Configuration)
-- 啟動 → 功能 → CLR 整合 → 勾選「啟用 CLR 整合」

C# 專案建立與程式碼實作

1. 建立專案

  • Visual Studio → **SQL Server 資料庫專案** 或 **類別庫** (目標 .NET Framework 2.0/3.5/4.x 視 SQL Server 版本)。
  • 加入參考:`Microsoft.SqlServer.Server` (位於 `C:\Program Files\Microsoft SQL Server\90\SDK\Assemblies\`)。

2. 核心程式碼:列舉資料夾檔案回傳結果集


using System;
using System.Data.SqlTypes;
using System.IO;
using Microsoft.SqlServer.Server;

public partial class StoredProcedures
{
    [SqlProcedure]
    public static void ListDirectoryFiles(string directoryPath)
    {
        // 1. 定義結果集結構 (SqlMetaData 陣列)
        SqlMetaData[] metaData = new SqlMetaData[]
        {
            new SqlMetaData("FileName", SqlDbType.NVarChar, 255),
            new SqlMetaData("FileSize", SqlDbType.BigInt),
            new SqlMetaData("CreationTime", SqlDbType.DateTime),
            new SqlMetaData("LastWriteTime", SqlDbType.DateTime),
            new SqlMetaData("Extension", SqlDbType.NVarChar, 50)
        };

        // 2. 建立 SqlDataRecord 可重複使用
        SqlDataRecord record = new SqlDataRecord(metaData);

        // 3. 宣告結果集開始
        SqlContext.Pipe.SendResultsStart(record);

        try
        {
            // 4. 驗證路徑
            if (!Directory.Exists(directoryPath))
            {
                SqlContext.Pipe.Send($"Directory not found: {directoryPath}");
                return;
            }

            // 5. 列舉檔案並逐筆送出
            foreach (string file in Directory.GetFiles(directoryPath))
            {
                FileInfo fi = new FileInfo(file);
                record.SetString(0, fi.Name);
                record.SetInt64(1, fi.Length);
                record.SetDateTime(2, fi.CreationTime);
                record.SetDateTime(3, fi.LastWriteTime);
                record.SetString(4, fi.Extension);

                SqlContext.Pipe.SendResultsRow(record);
            }
        }
        catch (Exception ex)
        {
            SqlContext.Pipe.Send($"Error: {ex.Message}");
        }
        finally
        {
            // 6. 宣告結果集結束 (必要!)
            SqlContext.Pipe.SendResultsEnd();
        }
    }
}

關鍵 API 說明

API用途
SqlContext.Pipe.SendResultsStart(SqlDataRecord)宣告結果集結構定義,開始串流模式
SqlDataRecord.SetXxx(ordinal, value)設定單一資料列的欄位值 (可重複使用同一 Record 物件)
SqlContext.Pipe.SendResultsRow(SqlDataRecord)送出單一資料列給用戶端
SqlContext.Pipe.SendResultsEnd()結束結果集串流,**必須呼叫**,否則用戶端掛起
SqlContext.Pipe.Send(string)送出訊息/錯誤 (對應 PRINT/RAISERROR),不影響結果集

部署至 SQL Server

  1. 建置專案產生 DLL (如 `FileUtils.dll`)。
  2. 在 SSMS 中執行部署腳本:

-- 1. 建立組件 (PERMISSION_SET = EXTERNAL_ACCESS 需資料庫 TRUSTWORTHY 或簽署非對稱金鑰)
CREATE ASSEMBLY FileUtils
FROM 'C:\Deploy\FileUtils.dll'
WITH PERMISSION_SET = EXTERNAL_ACCESS;
GO

-- 2. 建立預存程序包裝器
CREATE PROCEDURE usp_ListDirectoryFiles
    @DirectoryPath NVARCHAR(260)
AS EXTERNAL NAME FileUtils.[StoredProcedures].ListDirectoryFiles;
GO

權限集合選擇

  • SAFE (預設): 僅管理記憶體運算,無外部存取 (檔案、網路、登錄)。
  • EXTERNAL_ACCESS: 允許檔案、網路、登錄、環境變數存取。**需資料庫 `TRUSTWORTHY ON` 或組件簽署非對稱金鑰/憑證對應登入授權。**
  • UNSAFE: 完全信任 (Unmanaged Code, 靜態變數共享等),**極度謹慎使用**。

測試呼叫與結果


EXEC usp_ListDirectoryFiles 'C:\Windows\Temp';

輸出標準結果集:


FileName          FileSize  CreationTime          LastWriteTime         Extension
----------------- --------- --------------------- --------------------- ---------
test.txt          1024      2024-01-15 10:30:00   2024-01-15 10:30:00   .txt
log_20240115.log  4096      2024-01-15 08:00:00   2024-01-15 12:00:00   .log
...

進階模式與最佳實務

  • 資料表值函數 (TVF): 使用 `SqlFunction(FillRowMethodName="FillRow")` 回傳 `IEnumerable`,可直接用於 `SELECT * FROM dbo.fn_ListFiles('C:\Temp')`,更符合關聯語意。
  • 大量資料流式處理: `SendResultsRow` 即時串流,不佔用伺服器端記憶體緩衝,適合百萬列匯出。
  • 參數驗證與清理: 防止路徑遍歷攻擊 (`..\`),限制允許根目錄。
  • 例外處理: `try/catch/finally` 確保 `SendResultsEnd()` 必被呼叫。
  • 效能: CLR 呼叫開銷 ~微秒級,複雜運算 (正則、加密) 效能遠超 T-SQL;簡單 CRUD 應優先用 T-SQL。
  • 部署自動化: 使用 SSDT (SQL Server Data Tools) 專案,支援建置/部署/單元測試/原始碼控制一體化。

結語

CLR 預存程序透過 `SqlContext.Pipe.SendResultsStart/Row/End` 模式,能以 **標準結果集** 形式將 .NET 強大功能 (檔案系統、網路、運算) 無縫整合進 SQL Server。**關鍵在於:權限集合最小化 (EXTERNAL_ACCESS)、資料流式回傳避免記憶體壓力、嚴格參數驗證防禦注入、自動化部署管線保障品質。** 現代架構中,此類跨界操作亦可考慮移至應用層微服務,視延遲容忍度與治理策略決策。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved