代码改变世界

SQL2005 用户自定义类型

2010-04-20 16:05  calm_水手  阅读(1048)  评论(0编辑  收藏  举报

      之前对这个“用户自定义类型”只是有所耳闻,没有机会用过,也没有自己动手去写过。今天朋友让我写一个简单的“用户自定义类型”,呵呵,糗了。

      下面是目前对它的了解。

  在VS2008中新建项目选择“数据库”—“SQL Server项目”,上面的combobox选择".NET Framework2.0"

 

输入项目名称,选择好自己的项目所在文件夹,点解确认后,会提示:

选择要使用“用户自定义类型”的数据库,在“添加新引用”中找到目标数据库,点击“确定”即可。

进入项目后,在解决方案上“右键”—“添加”—“用户定义的类型”。如图所示

在这里面可以添加“用户定义的函数”、“存储过程”、“聚合”、“触发器”等。选择“用户定义的类型”,后便可看到可以编辑的代码层。

  下面,在进行编码前,我们来了解一些内容,呵呵,这些内容,也是我边做边查的。呵呵,很庆幸,碰到不会的东西,能查一查,问一问,呵呵,也是件很好的事情。

  1.实现“用户定义的类型”UDT的要求:

  • UDT 必须通过创建公共的 static(Microsoft Visual Basic 中为 Shared)Null 方法,在类或结构中实现 System.Data.SqlTypes.INullable 接口。默认情况下,SQL Server 是可识别 Null 的。这是为使在 UDT 中执行的代码能够识别 Null 值所必需的。
  • 这个对应的就是上面的代码 public struct TypeTest:INullable。需要实现Null性,对null性的处理,是在这里处理的。

     

    1   public static TypeTest Null
    2     {
    3         get
    4         {
    5             TypeTest h = new TypeTest();
    6             h.m_Null = true;
    7             return h;
    8         }
    9     }

      必须创建一个名为 IsNull 的属性,在从 CLR 代码中确定值是否为空值时,需要使用该属性。SQL Server 发现 UDT 的空值实例时,将使用正常的空值处理方法保持该实例。服务器不会将时间浪费在序列化或反序列化 UDT 上(如果不是必须),也不会将空间浪费在存储空值 UDT 上。这种空值检查在每次从 CLR 引入 UDT 时执行,这意味着始终可以使用 Transact-SQL IS NULL 结构检查空值 UDT。服务器也使用 IsNull 属性来测试实例是否为空值。服务器确定 UDT 为空值后,可以使用其本机空值处理方法。

     

  • UDT 必须包含支持从其进行分析的公共 static (或 SharedParse 方法以及用于转换到对象的字符串表示形式的 ToString 方法。
  • 该方法的处理,是在这段代码中进行处理的:

    1   public override string ToString()
    2     {
    3         // 用您的代码替换下列代码
    4         return "";
    5     }

     

    具有用户定义序列化格式的 UDT 必须实现 System.Data.IBinarySerialize 接口并提供 ReadWrite 方法。

    对Read和Write方法的实现是在实现接口IBinarySerialize时需要处理的。处理方法如下:

     

    代码
     1 
     2     public string result;
     3 
     4     public void Read(System.IO.BinaryReader r) 
     5     {
     6 
     7         this.result = r.ReadString();
     8     }
     9 
    10     public void Write(System.IO.BinaryWriter w) 
    11     {
    12 
    13         w.Write(this.result);
    14     }

     

    这段代码需要自己手动的写上去,实现Read和Write方法。

    下面是编写用户自定义类型的其他要求:

  • 该 UDT 必须实现 System.Xml.Serialization.IXmlSerializable或者所有公共字段和属性必须均属于 XML 可序列化类型或者使用 XmlIgnore 属性进行修饰(如果要求替代标准序列化)。
  • 一个 UDT 对象必须只存在一个序列化。如果序列化或反序列化例程识别了某一特定对象的多个表示形式,则验证将失败。
  • 为了确保服务器将字节顺序的比较用于 UDT 值,SqlUserDefinedTypeAttribute.IsByteOrdered 必须为 true
  • 在类中定义的 UDT 必须具有不采用任何参数的公共构造函数。您可以选择创建其他重载类构造函数。
  • 该 UDT 必须将数据元素作为公共字段或属性过程公开。
  • 公共名称不能长于 128 个字符,并且必须符合在标识符中定义的针对标识符的 SQL Server 命名规则。
  • sql_variant 列不能包含 UDT 的实例。
  • 继承的成员无法从 Transact-SQL 访问,因为 SQL Server 类型系统不知道 UDT 中的继承层次结构。但是,您可以在创建类的结构时使用继承,并且可以在该类型的托管代码实现方式中调用此类方法。
  • 成员不能被重载,但类构造函数除外。如果您创建某一重载方法,则在 SQL Server 中注册程序集或创建类型时将不会引发错误。在运行时将检测到重载的方法,而不是在创建类型时检测到。只要永不调用重载的方法,重载的方法就可以存在于类中。一旦您调用重载的方法,就会引发错误。
  • 任何 static(或 Shared)成员都必须声明为常量或声明为只读。静态成员将无法改变。
  • 从 SQL Server 2008 开始,如果 SqlUserDefinedTypeAttribute.MaxByteSize 字段设置为 -1,则序列化 UDT 在大小上可达到大对象 (LOB) 大小限制(目前为 2 GB)。该 UDT 的大小不能超过在 MaxByteSized 字段中指定的值。
  • 编写用户类型的本地序列化:

      为 UDT 选择正确的序列化属性取决于您正尝试创建的 UDT 的类型。 Native 序列化格式利用了一个非常简单的结构,使 SQL Server 能够在磁盘上存储 UDT 的有效本机表示形式。如果 UDT 为简单形式并且只包含以下类型的字段,则建议采用 Native 格式:

    bool、byte、sbyte、short、ushort、int、uint、long、ulong、float、double、SqlByte、SqlInt16、SqlInt32、SqlInt64、SqlDateTime、SqlSingle、SqlDouble、SqlMoney, SqlBoolean

      由上述类型的字段构成的值类型十分适合于 Native 格式,例如 C# 中的 structs(或者 Visual Basic 中已知的 Structures)。例如,用 Native 序列化格式指定的 UDT 可以包含也用 Native 格式指定的其他 UDT 的字段。如果 UDT 定义更复杂并且所包含的数据类型不在上述列表中,您必须改为指定 UserDefined 序列化格式。

    Native 格式具有以下要求:

    • 该类型不得指定 MaxByteSize 的值。

    • 所有字段必须都是可序列化的。

    • 如果 UDT 是在类中定义的并且不是结构,则必须将 StructLayoutAttribute 指定为 StructLayout.LayoutKindSequential。此属性控制数据字段的实际布局并用于强制成员按它们出现的顺序排列。SQL Server 使用此属性确定具有多个值的 UDT 的字段顺序。

    用户自定义序列化,在我们自己编写“用户自定义类型”时,需要使用UserDefined,即需要将系统自动生成的代码

    [Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native)]修改为:

    [Microsoft.SqlServer.Server.SqlUserDefinedType(Format.UserDefined)]

    SqlUserDefinedTypeAttribute 属性的 UserDefined 格式设置使开发人员能够完全控制二进制格式。在将 Format 特性属性指定为 UserDefined 时,您需要在代码中采取以下操作:

    • 指定可选的 IsByteOrdered 特性属性。默认值为 false。

    • 指定 SqlUserDefinedTypeAttribute 的 MaxByteSize 属性。

    • 通过实现 IBinarySerialize 接口,编写代码以便为 UDT 实现 Read 和 Write 方法。

    好了,上面的内容是我们再进行编写我们自己的“用户自定义类型”前需要了解的,呵呵。我也是了解了这么多,然后就开始试着写自己的“用户自定义类型”。例子比较简单,就是把输入的时间统一成一个时间格式。希望能达到理解“用户自定义类型”的目的。代码如下:

     

    代码
     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=4000)]
     9 public struct TypeTime : INullable,IBinarySerialize
    10 {
    11     #region//实现IBinarySerizlize的Read和Write方法
    12 
    13     public string result;
    14 
    15     public void Read(System.IO.BinaryReader r) 
    16     {
    17 
    18         this.result = r.ReadString();
    19     }
    20 
    21     public void Write(System.IO.BinaryWriter w) 
    22     {
    23 
    24         w.Write(this.result);
    25     }
    26 
    27     #endregion
    28 
    29     public override string ToString()
    30     {
    31         // 用您的代码替换下列代码
    32         return result;
    33     }
    34 
    35     public bool IsNull
    36     {
    37         get
    38         {
    39             // 在此处放置代码
    40             return m_Null;
    41         }
    42     }
    43 
    44     public static TypeTime Null
    45     {
    46         get
    47         {
    48             TypeTime h = new TypeTime();
    49             h.m_Null = true;
    50             return h;
    51         }
    52     }
    53 
    54     public static TypeTime Parse(SqlString s)
    55     {
    56         if (s.IsNull)
    57             return Null;
    58         TypeTime u = new TypeTime();
    59         // 在此处放置代码
    60         u.result = OneTime(s);
    61         return u;
    62     }
    63 
    64     /// <summary>
    65     /// 将输入的时间格式统一
    66     /// </summary>
    67     /// <param name="str"></param>
    68     /// <returns></returns>
    69     public static string OneTime(SqlString str)
    70     {
    71         return Convert.ToDateTime(str.Value).ToShortDateString() + Convert.ToDateTime(str.Value).ToShortTimeString();
    72     }
    73    
    74 
    75     // 私有成员
    76     private bool m_Null;
    77 }
    78 
    79 
    80 

     

    其中系统自动生成的两个方法

    代码
     1   // 这是占位符方法
     2     public string Method1()
     3     {
     4         //在此处插入方法代码
     5         return "Hello";
     6     }
     7 
     8     // 这是占位符静态方法
     9     public static SqlString Method2()
    10     {
    11         //在此处插入方法代码
    12         return new SqlString("Hello");
    13     }

    Method1和Method2可以删掉,替换成自己的方法。

    其中需要注意的问题有这么几个:

    1.[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.UserDefined,MaxByteSize=4000)]
    public struct TypeTest : INullable,IBinarySerialize  需要同时实现INullable和IBinarySeralize两个接口;

    2.实现IBinarySeralize的Read和Write两个方法;

    3.SqlString是数据库类型,使用的时候,需要使用它的.value属性,转换成数据类型string 才能使用。

    好了,这个问题被解决掉了。

    碰到问题的时候,要冷静,知道自己想要什么,剩下的就是想办法去拿;自己不知道怎么能拿到,呵呵,就看看谁拿到了,然后跟人家学就好了。话是这么说的,我也是这么告诉自己的,呵呵,有时候,我也会因为某个问题没能解决头大。慢慢来,告诉自己冷静就好,问题的产生,就是用来解决的。开心,好运!