七.Orchestration中从零开始新建一个xml类型的消息
一.概述
一般来讲,在biztalk项目中使用的消息都是基于xsd的xml消息,因为这是biztalk最擅长处理的消息。Biztalk整个体系架构都是建构在xml基础之上,在biztalk内部用xml来处理消息,在message box的存储形式也是xml。
但是这并不表示biztalk只能处理xml消息,事实上,biztalk可以处理消息类型可以是平面文件、xml类型、可序列化的.net class或者其他任意的二进制数据流。
这里要讨论的是在biztalk中,尤其是在orchestration中如何使用.net class类型消息。
二.消息的结构BizTalk中每条消息都可视为多部分消息,此消息可以由零个或多个部分组成。具有一个或多个部分的消息都具有一个标识为正文部分的部分(但是只能有个一部分标识为正文)。每个部分均由可表示XML 文档、平面文件、序列化的.NET 类或其他二进制数据流的二进制数据块组成。
一般的开发biztalk项目体验中,大多是新建一个消息,然后给这个消息指定一个xsd作为这个消息的类型。感觉上一个消息就只能指定一个xsd类型(也可以指定为一个序列化的.net class类型),跟一个xsd相关。其实这样通过这样方式建立的消息也是多部分消息,只是这个消息只有一个正文部分而已。
如果要建立真正有多个部分的消息类型,可以在orchestration view面板中的Types窗格中,展开Types,在Multi-part Message Types下新建一个多部分消息类型,在新建的多部分消息类型中可以建立多个部分,每个部分分别指定类型,还要指定其中的一个部分为正文部分。以后新建消息把消息类型指定为这个多部分消息类型,这个新建消息就是一个具有多个部分的消息了。
注意 |
任何消息在biztalk的表现都是一个多部分的消息,不过一般的消息只有一个正文部分。 |
从上面的叙述可以得知,每个消息其实都是多部分消息,一般来讲每个消息至少有一个部分,也可能有多个部分,不管是只有一个部分还是有多个部分,其中必须有一个部分被指定为正文部分,整个消息的类型就由正文部分的消息类型决定。如果正文部分分别为以下情况时,如何确定消息的类型:
1.平面文件
平面文件进入到biztalk都会在接收管道中都会被平面文件拆装器拆解为xml的格式,有一个经过平面文件扩展后的xsd架构跟其对应,所以平面文件可以被认为也是xml的消息类型。参看下面的xml文档部分。
2.Xml文档
Xml消息的消息类型由xml消息的命名空间(后接# 符号)和xml消息根节点的名称组成。比如,一个xml消息的根结点的名称空间是“http://tempuri.org/samples/MessageType”,xml消息的根结点的名称是Message,那么这个消息的类型就是:http://tempuri.org/samples/MessageType#Message
3..net class
.net class类型的消息同样是由名称空间加上根元素名称组成。
.net class类型的消息的名称空间由class类的System.Xml.Serialization.XmlRootAttribute属性指定,在定义.net class类时可以在类名前加上相关attribute来指定这个类的一些类级别的attribute(请参考第五部分“.net class类型消息”中的详细定义一个.net class的完整代码),System.Xml.Serialization.XmlRootAttribute中用Namespace参数指定此类的名称空间。
定义.net class的类名就相当于xml类型的根元素。
“名称空间#类名”就是.net class消息的消息类型,跟一般xml消息一样,.net class消息类型也可以作为订阅消息的条件。
4.二进制数据
二进制数据流的消息类型需要由定制的接收管道把消息类型升级到消息的上下文,一般的二进制数据流的消息类型使用全限定的类型名。
这种情况这里不加讨论,如有必要,以后用专文来探讨二进制数据流消息的情况。
四..net class类型的序列化Biztalk中的消息必须是可以序列化的,原因很简单,biztalk中消息不管是从外部进来,还是由orchestration产生的消息,统统都要被保存在Message Box数据库中,要能被保存在数据库中持久保存,消息就必须能被序列化。
还有一点,在长时间运行的orchestration中,运行的周期可能是一天、几天、十几天,甚至可能几个月。在这么长的时间中,一个orchestration实例由始至终都运行在内存中是及其不明智的做法,将耗费大量的资源。所以biztalk设计了一个dehydrat(分解)和rehydrat(重组)的机制,在orchestration实例空闲的时候,把orchestration实例和相关的状态和消息序列化保存到数据库,一旦有消息激活这个orchestration实例时,再把这个orchestration实例和相关状态消息反序列化到内存继续运行。
对.net class序列化Dotnet框架支持两种序列化方式,可以把类序列化为xml格式,也可以把类序列化为二进制格式。
二进制序列化是把对象转换成一串二进制的数据流,二进制序列化保持类型保真度,可以最真实的保存一个对象的全部状态。
Xml序列化是把对象转换成一个xml文档,XML 序列化仅序列化公共属性和字段,且不保持类型保真度。但对于biztalk应用中使用的.net class一般就是使用了类的公共属性和字段。
Biztalk内建直接支持.net class序列化,就是说在biztalk中使用.net class的消息不用开发者自己考虑.net class消息如何如何序列化和反序列化。Biztalk内建.net class消息序列化使用的是xml序列化方式。
二进制序列化方式不是biztalk内建支持的序列化方式,可以选择对.net class消息进行二进制的序列化,但是这需要开发者做更多的工作,需要写代码对消息进行二进制的序列化和反序列化,并定制自己的拆装器和组装器对消息进行序列化和反序列化的工作。
本文只讨论xml序列化的方式,对于二进制序列化的方式,如有必要以后再用专文讨论。
五..net class类型的消息Biztalk中使用.net class需要考虑作为消息的特点,跟一般的类有些不同,一般要注意以下几点:
1.类要设置为可序列化
在定义类前加上表示可以序列化的attribute:[Serializable()]
2.类中只包含公共属性和字段
一般类中的公共属性和字段会被转换为xml文档中的元素,私有属性没什么意义,方法对转成xml也没什么意义。
3.设置名称空间
名称空间是构成消息类型的一部分,.net class转成xml也可以带有名称空间,看下面的代码,名称空间由定义类前的“[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://myURL")]”设置,表示这个类转成xml的Target Namespace是"http://myURL"。
另外类的名称就相当于xml的根元素名称,跟上面的名称空间一起组合成消息的类型。
如果没有设置名称空间,则此.net class消息的类型就是类名称。
4.设置可分辨字段和升级属性
在.net class类型中同样可以设置可分辨字段和升级字段。
4.1.设置可分辨字段
设置可分辨字段很简单,在需要设置的公共属性或字段前加上“[DistinguishedField]” attribute即可,表示这个公共属性或字段已被设置为可分辨字段。
4.2.设置升级属性
设置升级属性同xsd中设置升级属性类似,首先也先有一个属性架构,然后在需要升级为属性的公共属性或字段前用“Property”attribute指定关联到哪个属性。如下面的代码中的:
[Property(typeof(netClassMessage_PromoteXSD.PropertyName))]
public string PropertyName
这表示要把公共属性PropertyName升级,并关联到属性架构netClassMessage_PromoteXSD中的PropertyName属性。
5.一个.net class消息类型的完整代码
6//表示此类是可以序列化的7[Serializable()]
8//此属性设置目标名称空间TargetNamespace9[System.Xml.Serialization.XmlRootAttribute(Namespace="http://myURL")]
10//类名称相当于XSD中的根元素,上面设定的名称空间加上类名称就组成了消息类型11publicclassnetClass
12{
13privatestring_ID;
14privatestring_name;
15privatestring_address;
16privatestring_propertyName;
17publicnetClass()
18{
19_ID=System.Guid.NewGuid().ToString();
20}21//DistinguishedField属性表示这个字段是可分辨字段22[DistinguishedField]
23publicstringID
24{
25get26{
27return_ID;
28}29set30{
31_ID=value;
32}33}34[DistinguishedField]
35publicstringName
36{
37get38{
39return_name;
40}41set42{
43_name=value;
44}45}46[DistinguishedField]
47publicstringAddress
48{
49get50{
51return_address;
52}53set54{
55_address=value;
56}57}58[DistinguishedField]
59//netClassMessage_PromoteXSD.PropertyName是在属性schema中定义的属性,此Property属性
60//指示这个字段为promote字段,对应netClassMessage_PromoteXSD.PropertyName61[Property(typeof(netClassMessage_PromoteXSD.PropertyName))]
62publicstringPropertyName
63{
64get65{
66return_propertyName;
67}68set69{
70_propertyName=value;
71}72}73}74}
.net class类型的消息从外部进入biztalk,.net class类型消息在进入到biztalk时的存在形式必定是被序列化后的xml形式(这里只讨论xml序列化的情况)。
所以,对入站的.net class类型消息需要使用xml拆装器,.net class类型消息序列化后同样有名称空间和根元素,并且也跟一般的xml消息类型一样,名称空间和根元素也决定了这个.net class消息的消息类型。
同样,.net class消息根一般xml消息一样根据消息类型被路由到订阅此消息的orchestration。
.net class消息在orchestration中的存在形式还是xml形式,只有在消息构造形状中对.net class消息进行构造或赋值的时候,biztalk引擎会对消息做相关的序列化和反序列化的工作,以便对.net class消息进行相关的处理。在消息构造形状以外,.net class消息的存在形式就是xml。
七.Orchestration中从零开始新建一个xml类型的消息在orchestration,一个消息一旦被构造完成,这个消息就不再被允许有任何改动,就是不能被修改了。如果要修改这个消息,必须重新在消息构造形状中构造一个新消息,然后修改新的消息,新消息一旦出了构造形状就算构造完成。
构造xml消息和构造.net class类型消息有所不同,下面分别列出
1.在消息构造形状中构造新xml消息
l通过map映射,map映射把源架构的消息映射到目标架构的消息,在转换的同时把消息值赋给了新的目标架构的消息,构造了一个新的消息。
l通过消息赋值语句,赋值语句把同类型的消息赋给一个新的消息,构造了新消息。
l通过把XmlDocument赋值给消息,xml消息的本质就是一个xml,所以可以把一个XmlDocument类型的变量直接赋给xml消息,当然,这个XmlDocument要是跟这个消息兼容的,XmlDocument要符合这个xml消息的架构。
2.在消息构造形状中构造新.net class类型消息
只能通过new操作符构造新.net class类型消息,.net class类型消息首先它是一个.net的类,可以通过new操作符来新建一个此类的对象。
3.从零开始构造新xml类型消息
如果一个.net class类型消息从外部进入到orchestration,在orchestration中对这个.net class类型消息进行处理转换,无外乎会有两种情况:一种是把这个消息赋值给同类的或者另一个.net class的消息,然后对新消息进行处理。更多的情况是,要把这个.net class消息转换成一个xml的消息,然后处理这个xml消息。
对于第一种情况简单,只要用new操作符新建一个.net class类型的消息,然后进行赋值或者修改新消息的属性等等即可进行相关处理。
但是对于第二种情况就比较麻烦,用map吧,map只支持从xml消息到xml消息的转换,不支持.net class消息的转换。用赋值语句吧,xml类型和.net class类型不是同类型,不能直接赋值。如果xml消息类型可以用new操作符新建一个新消息多好,可是,目前还不行,不能跟一般的.net类一样通过new操作符来新建一个对象。
但是这里又必须要先新建一个xml类型的消息,有了这个xml消息,才能把.net class类型的消息的相关属性转移到这个xml类型的消息。
从零新建一个xml类型消息(from scratch)在biztalk中是个比较麻烦的事,上面提到的通过把XmlDocument赋值给xml消息好像是目前的唯一方法。
这种情况大致需要做以下的事情:
在orchestration中新建一个XmlDocument类型的变量,比如变量名为xmlDocVar,要构造的xml消息为xmlMsg,然后在消息赋值形状表达式中
xmlDocVar.LoadXml(“xml字符串”);
xmlMsg = xmlDocVar;
其中的“xml字符串”是一个跟xmlMsg消息的架构相容的一个xml样例的文本。赋值以后,就可以对xmlMsg进行各种操作了,把可分辨字段或升级属性修改为需要的值。
把xml样例文本直接放在硬代码里面不太方便,也不灵活,比较好的做法是把xml文本放在一个xml文件中,从文件中读取xml文本。
上面的例子可以变成这样:
xmlDocVar.LoadXml(“xml文件名”);
xmlMsg = xmlDocVar;
其中的“xml文件名”是xml样例文件的完整路径,这样如果要修改xml样例文本直接修改这个文件就行了,不用去改硬代码。
但是这样会给部署带来问题,项目部署以后这个xml文件往哪放,biztalk项目其他的资源都是dll,被部署到GAC中,就这么个xml文件孤零零的要找个地方放,虽说也没什么问题,但总觉得不很舒服,至少这个办法不够优雅。
其实还有一个更舒服的办法,就是把这个xml文件作为一个嵌入资源,把它嵌入到项目生成的dll中,一起被部署到GAC中,读取的时候也到这个dll中读取。这需要做些额外的编码工作,步骤如下:
3.1.设置xml文件属性
在解决方案资源管理其中,选中这个xml文件,查看它的属性,把“生成操作”这一项,改成“嵌入的资源”,这样这个xml文件就会被嵌入到编后的dll文件中。
3.2.写一个从嵌入资源中读取xml文件内容的方法
在跟这个xml文件同一个项目中,新建一个类,这个类只有一个静态方法,这个方法根据提供的xml文件名,在assembly中找到嵌入的xml文件,返回这个xml文件对应的XmlDocument的对象。
这个方法的完整代码如下:
9publicclassEmbeddedResourseProcessor
10{
11publicstaticXmlDocumentGetXmlDocResource(stringfileName)
12{
13XmlDocumentdoc=null;
14Typetype=MethodBase.GetCurrentMethod().DeclaringType;
15Assembly_assembly=Assembly.GetExecutingAssembly();
16string_namespace=type.Namespace;
17
18stringresourceName=_namespace+"."+fileName;
19
20Streamstream=_assembly.GetManifestResourceStream(resourceName);
21doc=newXmlDocument();
22doc.Load(stream);
23returndoc;
24}25}26}
3.3.在消息赋值形状中调用读取xml文件的方法
在orchestration的消息赋值形状中,用类似下面的语句给新xml消息赋值:
XmlMsg = netClassMessage_netClass.EmbeddedResourseProcessor.GetXmlDocResource("xml文件名");
参考资料:
lUsing .NET Classes for Orchestration Message Types
lBizTalk messages based on .NET types instead of Xsd schemas
lConstructing BizTalk 2004 XML Messages (In an Orchestration)