在SQL Server 2005中添加了对CLR的支持,这使得我们可以使用C#,VB.NET等语言为SQL Server编写函数、存储过程以及触发器等对象。如何创建这些对象就不多说了,在网上搜一搜都有很多。这里就说一下在创建聚合函数的时候一些值得注意的问题。
自定义聚合函数是以一个值类型对象的形式来实现的,这个对象必须被序列化到数据库中。如果该对象有int或double等值类型的字段,那么几乎不会产生问题。但是如果有string等类类型的字段,在SQL Server中创建聚合函数的时候很有可能会出现类似下面的6225号错误消息;
对类型 SqlServerProject.Aggregate 做标记以进行本机序列化,但是类型 Aggregate 的字段 result 为 string 类型(它是非值类型)。本机序列化类型只能有可直接复制到本机结构中的字段类型。如果希望有任何其他类型的字段,请考虑使用其他的序列化格式,如用户定义序列化。
出现这个错误的原因是,对于int、double等类型的数据,它们直接对应操作系统使用的本机数据类型,例如int和double在C++中都有相应的类型,实际上在C#和C++中它们的结构都是一样的,因此在序列化的时候可以将这些类型的字段当作本机类型来处理。而对于类类型的字段,例如string,在操作系统中没有对应的数据类型,因此这些字段不能直接序列化。用户必须手动添加序列化代码,告诉SQL Server如何去序列化这些类型的字段。
我们所要做的工作非常简单,只需要为聚合函数对象实现IBinarySerialize接口即可。例如:
private string result;
#region IBinarySerialize 成员
public void Read(System.IO.BinaryReader r) {
this.result = r.ReadString();
}
public void Write(System.IO.BinaryWriter w) {
w.Write(this.result);
}
#endregion
}
IBinarySerialze接口有两个方法,Read()方法从序列化流中还原字段,也就是把二进制数据转换成string,这里我们不必手动进行转换,因为该方法的BinaryReader类型的参数已经提供了一系列进行转换的方法,使用Read前缀的方法可以从二进制流中转换出不同类型的值。而Write()方法用于把字段写入序列化流,也就是将字段转换成二进制数据,同理,BinaryWriter参数也已经提供了一系列进行转换的方法,直接调用即可。
此时问题似乎解决了,不过再次创建聚合函数,又可能会出现下面的的6222号错误:
对类型 SqlServerProject.Aggregate 做标记以进行本机序列化,但是类型 Aggregate 的字段 result 对于本机序列化无效。
这段话让人摸不着头脑,我费了好大劲也搞不出个所以然。不过最后原因还是找到了。看到聚合函数对象的声明上面那句代码:
这是VS自动为我们生成的,可是问题也恰恰是由这句代码引起的。只要我们把Format后面的值改为这样:
问题就解决了。原因跟上一个问题很相似,Format.Native的意思是使用本机数据类型来保存这个实现聚合函数的值对象,只有当该对象中的字段都有对应的本机数据类型时才有效,例如int,long等。由于我在这个对象中使用了string类型的字段,导致问题发生。
Format.UserDefined表示对象中的字段使用的是用户定义类型,也就是没有相应本机类型的数据类型。string满足该条件,所以要把Native改成UserDefined。