在開發 UDT 時,若包含 string 等參考型別欄位,會遇到序列化錯誤:
「類型標記為原生序列化,但欄位 'ageGroup' 為 string 型別(非實值型別)...」
本文詳細解析錯誤成因與解決方案。
基本實值型別(int、double 等)在 C# 與 C++ 中記憶體配置相同,SQL Server 可直接以原生方式序列化。
string 是參考型別,在作業系統層級無對應的原生資料結構。SQL Server 無法自動推斷其序列化方式,開發者必須透過 IBinarySerialize 明確指定讀寫邏輯。
public void Read(System.IO.BinaryReader r)
{
this.ageGroup = r.ReadString();
}
public void Write(System.IO.BinaryWriter w)
{
w.Write(this.ageGroup);
}
// 原本自動產生
[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native)]
// 修改為
[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.UserDefined, MaxByteSize = 10)]
MaxByteSize 不是字串長度,而是序列化後的總位元組數。以 10 個 Unicode 字元為例:
MaxByteSize = 22 或略大於此值本範例設為 10 屬於保守估計,實際部署時請依據最大可能輸出計算。
[Serializable]
[SqlUserDefinedType(Format.UserDefined, MaxByteSize = 10)]
public struct AgeGroup : INullable, IBinarySerialize
{
// 核心欄位
public string ageGroup;
private bool m_Null;
// INullable 實作
public bool IsNull { get { return m_Null; } }
public static AgeGroup Null { get { ... } }
// Parse / ToString
public static AgeGroup Parse(SqlString s) { ... }
public override string ToString() { return ageGroup; }
// 核心邏輯:年齡區間判斷
public static string ComputeAgeGroup(string s) { ... }
// IBinarySerialize 實作
public void Read(BinaryReader r) { this.ageGroup = r.ReadString(); }
public void Write(BinaryWriter w) { w.Write(this.ageGroup); }
}
SELECT CAST('25' AS AgeGroup);處理參考型別 UDT 的關鍵在於:理解序列化機制差異、正確實作 IBinarySerialize、精確計算 MaxByteSize。掌握這三點,即可突破原生序列化的限制,開發功能豐富的自訂型別。