學習 SQL Server CLR 沒有多久,就遇到了一個讓我很郁悶的問題;如何處理當“用戶定義類型”中因出現像 string 這樣的引用類型而引發地一些問題?
下面是我經過查詢相關資料而得出的解決方法(代碼所示):
用戶定義類型
1 using System;
2 using System.Data;
3 using System.Data.SqlClIEnt;
4 using System.Data.SqlTypes;
5 using Microsoft.SqlServer.Server;
6
7 [Serializable]
8 [Microsoft.SqlServer.Server.SqlUserDefinedType(Format.UserDefined, MaxByteSize=10)]
9 public struct AgeGroup : INullable, IBinarySerialize
10 {
11 public override string ToString()
12 {
13 return ageGroup;
14 }
15
16 #region 自動生成
17
18 public bool IsNull
19 {
20 get
21 {
22 return m_Null;
23 }
24 }
25
26 public static AgeGroup Null
27 {
28 get
29 {
30 AgeGroup h = new AgeGroup();
31 h.m_Null = true;
32 return h;
33 }
34 }
35
36 #endregion
37
38 public static AgeGroup Parse(SqlString s)
39 {
40 if (s.IsNull)
41 return Null;
42 AgeGroup u = new AgeGroup();
43 u.ageGroup = AgeGroup.ComputeAgeGroup(s.Value);
44 return u;
45 }
46
47 /// <summary>
48 /// 計算年齡段
49 /// </summary>
50 /// <param name="s">用戶輸入字串</param>
51 /// <returns></returns>
52 public static string ComputeAgeGroup(string s)
53 {
54 int tempInt = -1;
55
56 if (int.TryParse(s, out tempInt))
57 {
58 if (tempInt >= 0)
59 {
60 if (tempInt <= 6)
61 {
62 return "童年";
63 }
64 else if (tempInt > 6 && tempInt <= 17)
65 {
66 return "少年";
67 }
68 else if (tempInt > 17 && tempInt <= 40)
69 {
70 return "青年";
71 }
72 else if (tempInt > 40 && tempInt <= 65)
73 {
74 return "中年";
75 }
76 else
77 {
78 return "老年";
79 }
80 }
81 else
82 {
83 return string.Empty;
84 }
85 }
86 else
87 {
88 return string.Empty;
89 }
90 }
91
92 // 年齡段名稱
93 public string ageGroup;
94 // 標示是否為空(自動生成)
95 private bool m_Null;
96
97 #region IBinarySerialize 成員
98
99 public void Read(System.IO.BinaryReader r)
100 {
101 this.ageGroup = r.ReadString();
102 }
103
104 public void Write(System.IO.BinaryWriter w)
105 {
106 w.Write(this.ageGroup);
107 }
108
109 #endregion
110 }
111
112
113
因為“用戶定義類型”是以值類型對象的形式來實現的,所以當有像 string 這樣的引用類型的時候就會有
對類型 "Demo_SQLSERVER.AgeGroup" 做標記以進行本機序列化,但是類型 "Demo_SQLSERVER.AgeGroup" 的字段
"ageGroup" 為 string 類型(它是非值類型)...
類似這樣的錯誤信息提示 。
出現這個錯誤的原因是,對於int、double等類型的數據,它們直接對應操作系統使用的本機數據類型,例如int和double在C++中都有相應的 類型,實際上在C#和C++中它們的結構都是一樣的,因此在序列化的時候可以將這些類型的字段當作本機類型來處理。而對於類類型的字段,例如 string,在操作系統中沒有對應的數據類型,因此這些字段不能直接序列化。用戶必須手動添加序列化代碼,告訴SQL Server如何去序列化這些類型的字段。
因此,需要實現 IBinarySerialize 接口以告訴 SQL Server 如何去實例化指定的字段。
然而問題又來了:
對類型 "Demo_SQLSERVER.AgeGroup" 做標記以進行本機序列化,但是類型 "Demo_SQLSERVER.AgeGroup" 的字段 "ageGroup" 對於本機序列化無效。
我們還需要修改 VS 自動給我們生成的特性:
[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native)]
修改為:
[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.UserDefined, MaxByteSize=10)]
在這裡,對於用戶自定義的格式序列化,指定 MaxByteSize 屬性(表示聚合實例的最大大小的 Int32 值)是必須的;另外,此屬性的最大允許值 (8000) 由 MaxByteSizeValue 字段指定。
需要注意的是,對於指定了用戶定義的序列化的聚合,MaxByteSize 是指序列化的數據的總大小。以一個序列化包含 10 個字符 (Char) 的字符串的聚合為例。當使用 BinaryWriter 序列化該字符串時,序列化後的字符串的總大小為 22 個字節:每個 Unicode UTF-16 字符占據的字節數 2 乘以最大字符數 10,再加上序列化二進制流所引入的開銷占用的 2 個控制字節。因此,在確定 MaxByteSize 的值時,必須考慮序列化的數據的總大小:二進制形式的序列化數據的大小加上序列化引入的開銷。
到這裡,就可以“生成”一下,“部署”到SQL Server 實例裡使用自己定義的類型了。
參考資料:
http://technet.microsoft.com/zh-cn/library/ms131069.ASPx
http://msdn.microsoft.com/zh-cn/library/microsoft.sqlserver.server.sqluserdefinedaggregateattribute.maxbytesize%28VS.80%29.ASPx
http://kb.cnblogs.com/a/1546876/
如有不妥之處,還望不吝賜教。
如有轉載請注明出處謝謝合作!!!
簽名:做一番一生引以為豪的事業;找一個一生榮辱與共的妻子;在有生之年報答幫過我的人;並有能力幫助需要幫助的人;
博客園主頁歡迎大家過來交流探討:http://sufei.cnblogs.com/
QQ:361983679 Email:[email protected] MSN:[email protected]