montaque

小碗喝酒,小口吃肉

博客园 首页 新随笔 联系 订阅 管理
  111 Posts :: 1 Stories :: 618 Comments :: 9 Trackbacks

2008年8月12日 #

在 Silverlight 中,有几种获取后台数据的方式。 通过会有一个Service来暴露这些数据,对于sl2 这个service可以是最简单的ASMX或者用WCF的basichttpbinding, 您可以通过添加引用来生成代理,来适用这个service。

 

如果只是简单的数据的话,你也可以通过访问一个远程的XML文件来获取这些数据,拿到XML之后,需要把他转换成对象。也有几种方式

  • 用xmlReader
  • 用LinqForXml
  • 用系统解析XAML的方式,也就是本文中提到的方式。其实是最简单的一种方式。

思路:

首先定义个对像,我们知道在WPF/Silverlight 的class Resource中可以加入资源,资源可以是任意的对象。

而在程序中,可以通过访问资源Key的方式拿到这些对象。事实上,他拿这些对象的时候,就是适用一个简单的XamlReader来反序列化之。

 

一下是一个简单的Demo

 

假定有一下Class

 

Code

 

这时候你可以在resource中,加上一个自己的xmlns前缀,指向这个class所有的namespace和assembly

比如在Xaml中你可以这样些,这时候有很好的智能提示。

 

Code

 

然后我们新建一个xml文件,把我们写好的xaml copy进去。删掉一些不必要的属性,不如Key。 和 class

变成一下xml

 

Code

 

然后用一下简单的代码就可以反序列化得到对象的访问。

 

 

Code

 

如果是Sl, 把LoadAsyc改为Load就可以了。

如果正式应用的话,可以把Xml放在服务端,然后通过webclient download,然后调用以上代码做反序列化。

 

然后你就可以做正常的binding,Animation了,当然要正常工作的话,属性要改为DependencyProperty

 

 

posted @ 2008-08-12 08:59 montaque 阅读(1378) | 评论 (2)编辑

2008年8月6日 #

Expression blend 2.5 是目前的最新版本用来支持WPF和Silverlight Beta2 的开发。一般我们可视化的拖放和设计界面和效果。有时候如果你要人为的编辑一下Xaml的话发现没有智能提示非常不方便。有些属性很长的话,很难记住。

由于Expression本省是个WPF程序, 当然也是标准的.net程序。所以扩展其实很容易,Expressoin提供了一个IAddIn接口,你只要实现该接口就可以了。

我上次提到的Kaxaml,免费的xaml编辑器有这个智能提示的功能,能否切换到Expression中呢

image

 

telerick的Stefan就是简单的提出Kaxaml的代码,然后实现接口. Expression的启动的时候加上这个DLL,就有智能提示的功能了。

 

插件下载地址:http://code.msdn.microsoft.com/Release/ProjectReleases.aspx?ProjectName=BlendSense&ReleaseId=1358

 

下载好解压到expression目录,注意只支持2.5 June版本。然后启动的时候加上个开关加载该DLL就好了。

Blend.exe -addin:Addins\Expression.Blend.IntelliSense.dll

image

posted @ 2008-08-06 07:50 montaque 阅读(1409) | 评论 (7)编辑

2008年8月2日 #

最近几天参加了IdentityMine 的一个Silverlight培训。 identityMine跟微软的公司很好,他做的很多Case都是Microsoft的。可以从www.identityMine.com 看到他们在WPF和Silverlight方面的一些case。

他们公司的人大概有三种,一种是Designer一种是开发人员。另外一个很重要的就是Integrator。做两者的沟通,培训的一个将是 Robby 就是一个integrator。很有designer的sense,技术有不错。

推荐他的blog, www.nerdplusart.com 他的blog还被列在sliverlight的showcase上面。

他还做了一个xaml 编辑工具 http://kaxaml.com/。 

对于开发人员,我们更多的考虑数据的集成,业务的设计。而很多界面和动画上的东西都会觉得很复杂,以前我觉得做一个小球在箱子理撞来撞去的应用觉得很复杂,要计算运动轨迹。。。

后来看了一个他们做的demo,超简单。代码如下,呵呵。就是xy按照不同的速度然后原路运动而已。一下是一个demo,呵呵


<Canvas.Resources>

            <EventTrigger x:Name="et" RoutedEvent="Canvas.Loaded">

            <BeginStoryboard >                

                <Storyboard>

                    <DoubleAnimation BeginTime="0" Duration="0:0:3" Storyboard.TargetName="red" 


Storyboard.TargetProperty="(Canvas.Left)" From="0" To="350" AutoReverse="True" RepeatBehavior="Forever" ></DoubleAnimation>

                    <DoubleAnimation BeginTime="0" Duration="0:0:2" Storyboard.TargetName="red" 


Storyboard.TargetProperty="(Canvas.Top)" From="0" To="350" AutoReverse="True" RepeatBehavior="Forever"></DoubleAnimation>

                    <DoubleAnimation BeginTime="0" Duration="0:0:3" Storyboard.TargetName="green" 


Storyboard.TargetProperty="(Canvas.Left)" From="50" To="350" AutoReverse="True" RepeatBehavior="Forever" ></DoubleAnimation>

                    <DoubleAnimation BeginTime="0" Duration="0:0:2" Storyboard.TargetName="green" 


Storyboard.TargetProperty="(Canvas.Top)" From="50" To="350" AutoReverse="True" RepeatBehavior="Forever"></DoubleAnimation>

                    <DoubleAnimation BeginTime="0" Duration="0:0:3" Storyboard.TargetName="blue" 


Storyboard.TargetProperty="(Canvas.Left)" From="100" To="350" AutoReverse="True" RepeatBehavior="Forever" ></DoubleAnimation>

                    <DoubleAnimation BeginTime="0" Duration="0:0:2" Storyboard.TargetName="blue" 


Storyboard.TargetProperty="(Canvas.Top)" From="100" To="350" AutoReverse="True" RepeatBehavior="Forever"></DoubleAnimation>   


                 

                    </Storyboard>

            </BeginStoryboard>

            </EventTrigger>


        </Canvas.Resources>

<Ellipse Height="50" Width="50" Fill="#FFEE6C13"   StrokeThickness="0" x:Name="red" 


HorizontalAlignment="Left" VerticalAlignment="Top"/>

<Ellipse Width="50" Fill="#FFB3E80C"   StrokeThickness="0" Height="50" x:Name="green" 


HorizontalAlignment="Left" VerticalAlignment="Top"/>

<Ellipse Width="50" Fill="#FF09D1F0"   StrokeThickness="0" Height="50" x:Name="blue" 


HorizontalAlignment="Left" VerticalAlignment="Top"/>

</Canvas>

 

posted @ 2008-08-02 03:26 montaque 阅读(604) | 评论 (4)编辑

2008年6月10日 #

如何用写一个bat脚本返回昨天的年月日,其实用bat写很麻烦,你要写一个For 来拿到Date的年月日,然后再写一个小程序来推算昨天的年月日。其实要使能直接执行.net代码多好,.net Datetime.Now.AddDays(-1) 就是昨天了

 

其实Powershell就帮你回答这个问题,可以利用.net 类库丰富的功能支持,比如操作XML,文件IO,网络等特性。当然也可也自己来写一个类库,来实现复杂的脚本。

 

这时候,你打开powershell,敲入 [DateTime]::Now

则返回当前的日期,如果看看Now有哪些方法和属性

[DateTime]::Now | get-member

image

当然你要计算你长了多大的话,[DateTime]::Now-[DateTime]("yourbirthday")就返回你的年龄了,呵呵。我通常用这个来计算一下我baby几个月几天了。

 

powershell允许你定义一个变量,针对FTP下载的问题。你首先生成你要下载的文件名,然后整理到一个String,发给FTP就可以了。

以下是一个简单的例子。

$yersterday=[DateTime]::Now.AddDays(-1);
$yyyy=$yersterday.Year;
$mm=$yersterday.Month;
$day=$yersterday.Day;
$file1=[String]::Format("90193698_{0}{1:00}{2:00}_{0}{1:00}{2:00}.zip",$yyyy,$mm,$day); 

$cmd = "open yourftpserver
user username password

binary
get $file1"

$cmd | ftp -i -n

 

把他另存为一个PS1文件。 如果让他自动运行的话,写一个windows schedule task "powershell fullpathoftheps1"

默认poweshell有一个执行策略,不运行执行伟签名的文件。你可以改为

set-ExecutionPolicy RemoteSigned

或者直接改一下注册表

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell

image

 

或许这是个PS很好的一个实用例子吧。

posted @ 2008-06-10 02:27 montaque 阅读(2009) | 评论 (4)编辑

2008年5月30日 #

EDI in Biztalk 2006 R2 举了一个简单的例子。 通过裁剪满足特定vendor格式的EDI Schema,接受EDI转换成XML。

这时候的EDI很简单。

ISA*00*          *00*          *01*Fedex          *ZZ*Montaque       *070607*1555*U*00401*000000025*0*T*>~
GS*IN*Fedex*Montaque*20070607*1555*25*X*004010~
ST*810*0025~
N1*SF*COMPANY X~
N3*P.O. BOX 12345~
N4*ANYTOWN*OH*45678~
IT1*1*0.528*TS*1.13**VP*1AGHA1223221~
CTT*1~
SE*7*0025~
GE*1*25~
IEA*1*000000025~

只有一个Group,Group中只有一个Transaction Set。 如果包含多个TransactionSet,Biztalk是怎么处理呢

假设EDI文件改为以下格式。一个GS中,有三个TransactionSet。

ISA*00*          *00*          *01*Fedex          *ZZ*Montaque       *080529*0927*U*00401*000000014*0*T*>~
GS*IN*Fedex*Montaque*20080529*0927*14*T*00401~
ST*810*0014~
N1*SF*Montaque~
N3*16839 E Gale Ave. 91745~
N4*ANYTOWN*OH*45678~
IT1*1*0.528*TS*1.13**VP*1AGHA1223221~
CTT*1~
SE*7*0014~
ST*810*0015~
N1*SF*Montaque~
N3*16839 E Gale Ave. 91745~
N4*ANYTOWN*OH*45678~
IT1*1*0.528*TS*1.13**VP*1AGHA1223221~
CTT*1~
SE*7*0015~

ST*810*0016~
N1*SF*Montaque~
N3*16839 E Gale Ave. 91745~
N4*ANYTOWN*OH*45678~
IT1*1*0.528*TS*1.13**VP*1AGHA1223221~
CTT*1~
SE*7*0016~
GE*3*14~
IEA*1*000000014~

在Batch Inbound的时候,EDIReceiver Pipeline首先识别Party,然后找到该Party的Setting。默认是会Split成N个单独的消息。每个消息包含一个TransactionSet。

image 

如果把上面的EDI丢到Biztalk的话,他会产生三个消息。

每个消息包含一个TS。比如

image 

而我们也可以把它设置为。Preserver interchange,后面两个选项。告诉你出错的时候,刮起对应的Interchange还是TS。

这时候再处理的话,就会看大他把几个消息放在一个文件中。

 image

这个设置会牵涉到系统的设计。如果Vendor给一个包含10000个TS的EDI文件是启动一个Orchestration 实例处理,还是启动10000个。

一个的话,需要自己写一个Loop,而且load一个这么大的文件到内存。性能也不好。好处是实例数很少。

posted @ 2008-05-30 03:02 montaque 阅读(1089) | 评论 (1)编辑

Biztalk 可以做EAI,也可以做B2B。 做EAI的话,是企业内部的一个信息Hub或者总线。如果做B2B的话,则相当于企业对外的一个Gateway。不同的partner有不同的平台或者标准以及设置。所以biztalk除了做集成之外,还要去管理这些契约和元数据。

假定企业A使用EDI来跟不同的上下游做集成和信息交换。EDI经常会问几个问题?

  • 这家企业使用的是那个版本?EDI有好多版本。对应到Biztalk就是我收到这家企业的EDI之后,该用哪个Schema来解释
  • 使用X12还是EDIFact
  • 要不要处理double posting的问题。
  • Batch Inbound/outbound 处理。
  • 要不要ACK
  • 文件格式,换行时CR,还是CRLF等。

面对这个问题,Biztalk R2的EDI是通过一个新的Party识别机制来实现的。

每个Party都有具体的设定针对以上问题。Biztalk通过设别Party,从而应用该Party的设置。

我们看一下典型的EDI头。

ISA*00*          *00*          *01*Fedex          *ZZ*Montaque       *070607*1555*U*00401*000000025*0*T*>~

IS5-6事实上说sender是谁?比如*01*Fedex         

*ZZ*Montaque       * 则是说发给谁。 因为一家公司可能有不同的子公司。后面其实有00401 说明是什么版本。

Biztalk的Party识别就是基于这两个信息。首先看有没有一个party设置,完全匹配 Sender 和 receiver。

image

如果完全匹配,则使用该Party的设置。以上截图说明我们正好有个party叫Fedex。他的发送和接受完全匹配。设置其默认schema用来解析EDI。

一般来说一个party通常我们制定其Targetnamespace都一样。不同的文件,810还是850再做详细的剪裁。

然后如果Sender和Receiver不完全匹配。我们可能只指定Sender,Receiver为空。这时候适合一家公司下面有多个子公司,vendor发给你的时候,Biztalk作为Gateway忽略这些不同。

如果我们希望多个Sender来自不同的公司,使用一样的一组设置。那么可以设定QualifiedName

image

如果这些都没做,那么系统会默认使用Global Setting。

image

如何知道接收的消息所以那个Party呢? 一旦一个消息被解析为特定的一个party之后,该message会有一个PromotedProperty,叫做PartyName,比如下图所示。如果任何一个都没有被命中的话,则显示为Guestparty。

image

R2 EDI 很大程序上依赖这个party设置。包括AS2.  所以了解这个party解析算法很重要。

 

以上提到的是接受来自partner的EDI。 在party setting中称为Sender Setting。

如果是发送呢?

怎么知道该用哪个party的设置发送给Vendor?

这个正好反过来。你需要在系统中Promote PartyName,如果没有party的话,则会用Global的 Receiver Setting。

image

这里的Sender和Receiver是相对Biztalk 来说。 Sender 表示有个partner 发送消息给Biztalk。biztalk事实上是接受消息。这时候的IS5-8 是来识别Party。

Receiver表示有个partner需要加收Biztalk的消息。biztalk则是发消息给他。 这时候的IS5-8 是来制定EDI上的头。

posted @ 2008-05-30 02:51 montaque 阅读(1029) | 评论 (0)编辑

在biztalk 2006 R2中,EDI做了很大的加强。其中之一就是Party的识别算法和 EDI 由 Adpater 移到Pipeline中。

EDI 的schema在Biztalk R2中很大,C:\Program Files\Microsoft Biztalk Server\XSD_Schema\EDI 下面有一个压缩文件。加压后大概有4G多。

一把来说,你用哪个,把对应的schema从中加压出来。然后修改之,达到你想要的效果。一来默认的schema非常大,一个schema可能有5M左右,编译成Dll 翻倍,10M。 而.net assembly的大小不能大于75M,所以一般来说一个Dll只能包含5个左右的Schema。

这个时候,你就要小心剪辑你的Schema文件。

比如我有一个810 invoice的文件。如下,接下来我们一步一步的配置Biztalk ,解析成xml文件。这个EDI是版本00401,810.

ISA*00*          *00*          *01*Fedex          *ZZ*Montaque       *070607*1555*U*00401*000000025*0*T*>~
GS*IN*Fedex*Montaque*20070607*1555*25*X*004010~
ST*810*0025~
N1*SF*COMPANY X~
N3*P.O. BOX 12345~
N4*ANYTOWN*OH*45678~
IT1*1*0.528*TS*1.13**VP*1AGHA1223221~
CTT*1~
SE*7*0025~
GE*1*25~
IEA*1*000000025~

 

我们首先从schema压缩包中解压缩这个文件。

image

新建一个Biztalk的项目,把这个schema 添加进去。

image

这个schema还好1M多,接下来我们剪辑一下。假定Fedex给我们的文件,只需要ST,Nloop,CTT

我们把其他的都删除,你可以ctl多选或者shifit所中所有的节点。删除之,把他瘦身一下。

image

Biztalk是通过schema的TargetNamespace和RootElement来区分不同的schema。由于这个schema我们是专门针对Fedex的,所以不能直接部署到biztalk。我们把他的namespace有

http://schemas.microsoft.com/BizTalk/EDI/X12/2006

改为

http://schemas.microsoft.com/BizTalk/EDI/X12/2006/Fedex

image

然后选择项目属性,给他一个AssemblyKey,部署到EDIDemo这个Application中。然后点击部署

此时可以看到EDIDemo中有我们剪切好的Schema

image

 

Biztalk R2如果你Enable EDI的话,会默认帮你新建一个Biztalk EDI Application

用于处理EDI的系统Schema,Pipeline,等对象都在这个Application中。

默认我们的EDiDemo中并没有EDISend和EDIReceive的Pipiline

image

接下来右键EDIDemo Application,选择Add->Reference,EDI Application.这时候你就可以使用这些pipeline了。

我们首先新建一个Static one-way ReceivePort叫做 ReceivePort1

然后新建一个receivelocation,选择FileAdapter,PipeLine选择EDiReceive

image

然后Enable这个ReceiveLocation。

 

接着建立一个SendPort,设置一个Filter,如果从ReceivePort1来的消息,直接发出去。

image

设置一个Filter,这样不同写Orchastration来订阅这个消息。

 

image

这时候先Enlist这个sendport,注意不要Enable。为了调试方便。这样的话,你就可以看到将要发出什么消息出来。

 

这时候我们就可以发消息了。 把我们写好的EDI文件,Copy一份到接受目录。

这时候不巧,消息并没有出来。看一下系统日志,有个错误。错误如下:


Error encountered during parsing. The X12 transaction set with id '' contained in functional group with id '25', in interchange with id '000000025', with sender id 'Fedex          ', receiver id 'Montaque       ' is being suspended with following errors:

Error: 1 (Miscellaneous error)
    6: Missing or invalid or duplicate Transaction set identifier 'http://schemas.microsoft.com/BizTalk/EDI/X12/2006#X12_00401_810'

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

意思是说我们没有指定TransactionSet,这里的The X12 transaction set with id ''  id是空。哪里出错了呢?

接下来我们用Cordbg调试一下,一般来说在Biztalk里面使用EDI,很容易出这样那样的错误。尤其是EDI,出错了之后错误日志很模糊,由于Biztalk是个典型的.net application,我们可以通过查看异常的StackTrace,看到底发生什么了。

run Cordbg

Ca e

a [Biztalk的Process]

g

这时候再丢一个文件进去。cordbug就看到错误了。run De,退出调试,让程序直接走。

image

意思是说EDI的Pipeline,试图用://schemas.microsoft.com/BizTalk/EDI/X12/2006#X12_00401_810 来解析这个消息。可是这个schema并没有部署。为什么呢? 我们改了schema的namespace。 后面我会讲EDI得schema识别算法

EDI 有四种算法来解析改用那个schema。我们什么都没有设置,所以shema是由Global EDI Setting设置的

打开Party下面,右键Global EDI setting

image

果然,他设置的还是旧的那个Schema。我们把它改一下

改为 http://schemas.microsoft.com/BizTalk/EDI/X12/2006/Fedex

reboot一下Biztalk Service

然后确认,再丢一个消息进去。

打开biztalk的管理界面,查看一下suspended instance

就可以看到系统将要发出去的消息

image

你可以双击查看消息的明文

image

右键,resume这个instance。

image

然后接看到文件被发出来了。

image

 

当然这个只是一个Demo。可以写一个流程来处理该消息,比如写入数据库等。

我们剪切了一个Schema,针对一个vendor Fedex。然后配置了全局都使用这个schema。事实上可以配置Party,不同的party使用针对他们的schema

后面继续讲。

posted @ 2008-05-30 01:32 montaque 阅读(1067) | 评论 (1)编辑

2008年5月26日 #

Hello, Biztalk 2006 R2 BAM 中提到了如何配置一个BAM的Activity模型以及通过BAM API直接把关键业务数据喂给BAM。这个适合于企业中Legacy 系统的场景。

 

接下来我们还是以上次的HelloBamActivity模型为例,配置一个WCF程序。通过添加一个Behavior,定义一个IC(interceptor Configuration)拦截模型。把WCf运行过程中的一些关键业务数据喂给BAM。

 

我们首先写一个简单的WCF 程序。客户端发送一个订单到WCF 服务。订单包括State,和 Amount以及ID,这个订单的两个属性我们认为是个关键业务数据,需要提取出来反映在BAM中。

 

写一个WCF 程序。 由于WCF是基于消息的应用,不像Remoting,会有对象的跨Appdomain调用,写一个remoting应用需要两个appdomain,一般来说是两个应用程序。所以很简单,我就把WCF的Client和Service 都放在一个Console程序中。 为了调试方便,采用最简单的basicHttpBinding,没有安全。这样我们可以很轻松用TCPTrace,看到消息的传递过程。编译IC中的Xpath配置。

 

New一个Console Application叫做SingleWCF,添加对System.ServiceModel 和 System.Runtime.Serialization 的引用。

然后新建一个Class,定义Contract,Service实现。代码如下:

using System;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Threading;

namespace SingleWCF
{
    [DataContract()]
    public class PO
    {
        [DataMember]
        public float Amount;

        [DataMember]
        public string State;

        [DataMember]
        public string Id;
    }

    [ServiceContract()]
    public interface IPOService
    {
        [OperationContract()]
        string SubmitPo(PO po);
    }

    [ServiceBehavior(AddressFilterMode=AddressFilterMode.Any)]
    public class POServiceImpl : IPOService
    {

        #region IPOService Members

        public string SubmitPo(PO po)
        {
            Console.WriteLine("Service get the PO");
            Thread.Sleep(3000);
            Console.WriteLine("Service Processed the PO");
            return po.Id;
        }

        #endregion
    }
}

然后添加一个 APP.Config 文件。来Host这个Service。文件如下,

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
      <system.serviceModel>
               <behaviors>
                      <serviceBehaviors>
                <behavior name="enableWSDL">
                    <serviceMetadata httpGetEnabled="true" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <services>
            <service behaviorConfiguration="enableWSDL" name="SingleWCF.POServiceImpl">
                <endpoint address="ws" binding="basicHttpBinding" bindingConfiguration="basicBindingNoSecurity"
                    contract="SingleWCF.IPOService" behaviorConfiguration="bamBehavior" />
                <host>
                    <baseAddresses>
                        <add baseAddress="http://localhost:8888/service" />
                    </baseAddresses>
                </host>
            </service>
        </services>
      <client>
        <endpoint address=http://localhost:8888/service/ws behaviorConfiguration="bamBehavior" binding="basicHttpBinding" bindingConfiguration="basicBindingNoSecurity"
                   contract="SingleWCF.IPOService" name="clientendpoint" />
      </client>
      <bindings>
        <basicHttpBinding>
          <binding name="basicBindingNoSecurity">
            <security mode="None"/>
          </binding>
        </basicHttpBinding>
      </bindings>
    </system.serviceModel>
</configuration>

然后来Host Service并且调用。

using System;
using System.ServiceModel;
using System.Threading;

namespace SingleWCF
{
    class Program
    {
        static System.Threading.ManualResetEvent mre;
        static void Main(string[] args)
        {
             mre = new ManualResetEvent(false);
            System.Threading.ThreadPool.QueueUserWorkItem(StartHost);
            mre.WaitOne();
            System.Threading.ThreadPool.QueueUserWorkItem(StartClient);           
            Console.ReadLine();
        }

        public static void StartHost(object o)
        {
            ServiceHost host = new ServiceHost(typeof(POServiceImpl));
            host.Open();
            Console.WriteLine("HostStarted");
            mre.Set();
            Console.ReadLine();
        }

        public static void StartClient(object o)
        {
            ChannelFactory<IPOService> factory = new ChannelFactory<IPOService>("clientendpoint");

            IPOService client = factory.CreateChannel();
            PO p=new PO();
            p.Amount=120;
            p.State="WA";
            p.Id = Guid.NewGuid().ToString();
            client.SubmitPo(p);
            Console.WriteLine("PO Sent Done");

        }
    }
}

然后运行一下,就可以看到Service正常调用。

image

 

这时候把程序App.Config少许该一下,让客户端发送的目的地址由http://localhost:8888/service/ws 端口8888改为9999,然后TCPTrace负责forward9999到8888.这样客户和服务端之间的http通讯都可以被监视到。类似Soaptoolkit的monitor工具。

image

首先启动TCPTrace,可以从 http://www.pocketsoap.com/tcptrace/ 下载。

image


 

在运行程序,就可以在TCPTrace看到消息的调用。包括发送和接受的消息。

image

到这里我们的WCF service 就写好了。接下来需要配置我们的拦截模型IC,并且部署到BAM的配置库。

关于拦截模型。

首先需要定义一个事件源(EventSource),比如WCF程序是一个事件源。WF程序也是一个事件源。Biztalk也是事件源。

对于事件源,如果是.net 程序集,需要制定Assembly的fullname,如果是Biztalk,要制定是messagebox还是orchastration。

有了事件源,需要定义一个或者多个事件模型。

事件的定义,是通过Filter来筛选,事件发生后有要更改Activity的那些属性,通过Update来配置。另外还可以指定Reference,类似引用那个文档或者Activity。以及如何把多个事件源生成的Activity关联到一个的Continous模型。这个后面会讲。

对于事件源,一下是一个例子。

image

对于WCF,manifest是Contract的fullname,WF,则是workflow的fullname。

然后是一个事件的tree

image

 

接下来我们配置一个我们的IC.

我们需要配置的IC的事件源来自WCF。

当客户端发出一个请求的时候,并且是调用SubmitPo 的时候,我们称这个事件叫做BeginOrder。这时候拦截到State,Amount 属性。并且把当前的时间更新到Activity的BeginOrder。

当客户端收到submitPO的时候,我们成为事件EndOrder,更新Activity的EndOrder属性。

为了方便生成这个XML。Biztalk R2提供了三个Schema,分别面向WCF、WF,Common IC

WcfInterceptorConfiguration.xsd

CommonInterceptorConfiguration.xsd

这两个文件可以在SDK目录下面找到,如果没有的话,在安装盘Msi\Program Files\SDK\Samples\BIX 下面。可以把这两个文件copy到visual studio的schema 目录下面。方便有智能提示。

我们新建一个XML到项目中,成为wcfInterceptor.xml

<?xml version="1.0" encoding="utf-8" ?>
<bam:InterceptorConfiguration xmlns:bam="http://schemas.microsoft.com/BizTalkServer/2004/10/BAM/InterceptorConfiguration"
                          xmlns:bamwcf="http://schemas.microsoft.com/BizTalkServer/2004/10/BAM/WcfInterceptorConfiguration">
</bam:InterceptorConfiguration>

这时候按照智能提示,大多数配置可以轻松搞定。

定义一个事件源

<bam:EventSource Manifest="SingleWCF.IPOService, SingleWCF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" Name="wcfSource" Technology="WCF">
</bam:EventSource>

注意这里的manifest是fullname,wcf程序在运行的时候会根据当前endpoint所绑定的contract的全名去数据库中选招match的ic。如果manifest写错了的话,你就得不到任何BAM数据。

 

然后定义第一个事件。条件是:

ClientRequest 一个Operation是SubmitPo 

对应成语法是GetServiceContractCallPoint()=ClientRequest And GetOperationName()="SubmitPO"

IC 采用一个特殊的语法,叫做反向表达式。就是操作数在前,操作符在后。比如我们计算2+3×4 写作 234×+

WCF 有一些特殊的Operation,比如:

AutoGenerateCorrelationToken
GetContextProperty
GetEndpointName
GetOperationName //返回当前的调用的Contract的Operation
GetServiceContractCallPoint//调用拦截点。是ClientRequest还是ServiceReply
XPath//从message中抓数据。

这些可以从MSDN看到返回值和使用方式。

 

对应成ic的xml就是

<bam:OnEvent Name="BeginOrder" IsBegin="true" IsEnd="false" Source="wcfSource">
     <bam:Filter>
       <bam:Expression>
         <bamwcf:Operation Name="GetServiceContractCallPoint">           
         </bamwcf:Operation>
         <bam:Operation Name="Constant">
           <bam:Argument>ClientRequest</bam:Argument>
         </bam:Operation>
         <bam:Operation Name="Equals">           
         </bam:Operation>
         <bamwcf:Operation Name="GetOperationName">           
         </bamwcf:Operation>
         <bam:Operation Name="Constant">
           <bam:Argument>SubmitPo</bam:Argument>
         </bam:Operation>
         <bam:Operation Name="Equals">
         </bam:Operation>
         <bam:Operation Name="And"></bam:Operation>
       </bam:Expression>       
     </bam:Filter>
     <bam:CorrelationID>
       <bam:Expression>
         <bamwcf:Operation Name="XPath">
           <bamwcf:Argument>
             //*[local-name(.)='Id']
           </bamwcf:Argument>
         </bamwcf:Operation>
       </bam:Expression>
     </bam:CorrelationID>
     <bam:Update DataItemName="BeginOrder" Type="DATETIME">
       <bam:Expression>
         <bamwcf:Operation Name="GetContextProperty">
           <bamwcf:Argument>EventTime</bamwcf:Argument>
         </bamwcf:Operation>
       </bam:Expression>
     </bam:Update>
     <bam:Update DataItemName="State" Type="NVARCHAR">
       <bam:Expression>
         <bamwcf:Operation Name="XPath">
           <bamwcf:Argument>//*[local-name(.)='State']</bamwcf:Argument>
         </bamwcf:Operation>
       </bam:Expression>
     </bam:Update>
     <bam:Update DataItemName="Amount" Type="FLOAT">
       <bam:Expression>
         <bamwcf:Operation Name="XPath">
           <bamwcf:Argument>//*[local-name(.)='Amount']</bamwcf:Argument>
         </bamwcf:Operation>
       </bam:Expression>
     </bam:Update>
   </bam:OnEvent>

其中有一个element是CorrelationID,相当于Activity的PK值。不同的事件触发如何关联到一个Activity

接下来定义另外一个事件,服务返回。更新EndOrder时间。

<bam:OnEvent Name="EndOrder" IsBegin="false" IsEnd="true" Source="wcfSource">
     <bam:Filter>
       <bam:Expression>
         <bamwcf:Operation Name="GetServiceContractCallPoint">
         </bamwcf:Operation>
         <bam:Operation Name="Constant">
           <bam:Argument>ServiceReply</bam:Argument>
         </bam:Operation>
         <bam:Operation Name="Equals">
         </bam:Operation>
       </bam:Expression>
     </bam:Filter>
     <bam:CorrelationID>
       <bam:Expression>
         <bamwcf:Operation Name="XPath">
           <bamwcf:Argument>
             //*[local-name(.)='SubmitPoResult']
           </bamwcf:Argument>
         </bamwcf:Operation>
       </bam:Expression>
     </bam:CorrelationID>
     <bam:Update DataItemName="EndOrder" Type="DATETIME">
       <bam:Expression>
         <bamwcf:Operation Name="GetContextProperty">
           <bamwcf:Argument>EventTime</bamwcf:Argument>
         </bamwcf:Operation>
       </bam:Expression>
     </bam:Update>
   </bam:OnEvent>

 

请注意CorrelationID 的两个写法。从TCPTrace中可以看到这两个值对应一个请求。

写好这个IC之后,通过BM.exe部署

bm deploy-interceptor -filename:"xxx\SingleWCF\wcfInterceptor.xml"

这样IC模型就部署到BAM的元数据库中。你可以查询

image

数据中记录了该IC对应的Activity,以及事件的XML描述。

 

接下来我们要配置WCF程序,enable 一个BAM提供的EndpointBehavior,来横向拦截IC模型的数据。

右键 app.config,选择用EDIT with WCF Configuration.

Advance-> Extensions->new behavior element extension

image

新建一个Element 叫做bamEndpoinBehavior 名字可以自己随便输入。

Type 是Microsoft.BizTalk.Bam.Interceptors.Wcf.BamEndpointBehavior, Microsoft.BizTalk.Bam.Interceptors, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35

当然你可以点击Browser,定位到Gac中的Microsoft.BizTalk.Bam.Interceptors 程序集。

image

接下来新建一个EndpointBehavior叫做bamBehavior。把新建好的Element拖进去。配置

BAM数据连接字符串:指向本地BAM数据库

以及Polling的时间,写5,代表没5秒查询数据库看有没有新的IC定义。

然后配置service和client endpoint使用该behavior

image

此时完整的App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
      <system.serviceModel>
        <extensions>
            <behaviorExtensions>
                <add name="bamEndpointBehavior" type="Microsoft.BizTalk.Bam.Interceptors.Wcf.BamEndpointBehavior, Microsoft.BizTalk.Bam.Interceptors, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
            </behaviorExtensions>
        </extensions>
        <behaviors>
            <endpointBehaviors>
                <behavior name="bamBehavior">
                    <bamEndpointBehavior ConnectionString="Data Source=.;Initial Catalog=bamprimaryimport;Integrated Security=True"
                        PollingIntervalSec="5" />
                </behavior>
            </endpointBehaviors>
            <serviceBehaviors>
                <behavior name="enableWSDL">
                    <serviceMetadata httpGetEnabled="true" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <services>
            <service behaviorConfiguration="enableWSDL" name="SingleWCF.POServiceImpl">
                <endpoint address="ws" binding="basicHttpBinding" bindingConfiguration="basicBindingNoSecurity"
                    contract="SingleWCF.IPOService" behaviorConfiguration="bamBehavior" />
                <host>
                    <baseAddresses>
                        <add baseAddress="http://localhost:8888/service" />
                    </baseAddresses>
                </host>
            </service>
        </services>
      <client>
        <endpoint address="http://localhost:9999/service/ws"
                  behaviorConfiguration="bamBehavior" binding="basicHttpBinding"
                  bindingConfiguration="basicBindingNoSecurity"
                   contract="SingleWCF.IPOService" name="clientendpoint" />
      </client>
      <bindings>
        <basicHttpBinding>
          <binding name="basicBindingNoSecurity">
            <security mode="None"/>
          </binding>
        </basicHttpBinding>
      </bindings>
    </system.serviceModel>
</configuration>

 

大功告成了,Run 一下应用程序。然后打开BAM portal, http://localhost/bam 就可以看到数据进去了。

beginorder,endorder之间相差3秒左右。应为我们程序休息了3秒。 state是WA,amount是120.这些都是WCF 提供的数据。

image

 

如何调试知道Event有没有被命中呢? wcf intercepotr 采用.net标准的Trace模型。在App.config中加入

<system.diagnostics>
  <sources>
    <source name="Microsoft BizTalk Bam Interceptors" switchValue="All">
      <listeners>
        <add name="consolelistener" type="System.Diagnostics.ConsoleTraceListener"/>
      </listeners>
    </source>
  </sources>
</system.diagnostics>

注意SourceName必须是Microsoft BizTalk Bam Interceptors

我上次写成Microsoft Biztalk Bam Interceptors,结果什么也没有。debug了半天,hoho

再运行程序就看到结果了。一堆日志消息。

image

这样。WCF程序通过定义个interceptor模型,就可以把业务关键数据喂给bam了。

这里是完整的模型文件。

<?xml version="1.0" encoding="utf-8" ?>
<bam:InterceptorConfiguration xmlns:bam="http://schemas.microsoft.com/BizTalkServer/2004/10/BAM/InterceptorConfiguration"
                          xmlns:bamwcf="http://schemas.microsoft.com/BizTalkServer/2004/10/BAM/WcfInterceptorConfiguration">

  <bam:EventSource
    Manifest="SingleWCF.IPOService, SingleWCF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
    Name="wcfSource" Technology="WCF">
  </bam:EventSource>
  <bam:BamActivity Name="HelloBamActivity">
    <bam:OnEvent Name="BeginOrder" IsBegin="true" IsEnd="false" Source="wcfSource">
      <bam:Filter>
        <bam:Expression>
          <bamwcf:Operation Name="GetServiceContractCallPoint">           
          </bamwcf:Operation>
          <bam:Operation Name="Constant">
            <bam:Argument>ClientRequest</bam:Argument>
          </bam:Operation>
          <bam:Operation Name="Equals">           
          </bam:Operation>
          <bamwcf:Operation Name="GetOperationName">           
          </bamwcf:Operation>
          <bam:Operation Name="Constant">
            <bam:Argument>SubmitPo</bam:Argument>
          </bam:Operation>
          <bam:Operation Name="Equals">
          </bam:Operation>
          <bam:Operation Name="And"></bam:Operation>
        </bam:Expression>       
      </bam:Filter>
      <bam:CorrelationID>
        <bam:Expression>
          <bamwcf:Operation Name="XPath">
            <bamwcf:Argument>
              //*[local-name(.)='Id']
            </bamwcf:Argument>
          </bamwcf:Operation>
        </bam:Expression>
      </bam:CorrelationID>
      <bam:Update DataItemName="BeginOrder" Type="DATETIME">
        <bam:Expression>
          <bamwcf:Operation Name="GetContextProperty">
            <bamwcf:Argument>EventTime</bamwcf:Argument>
          </bamwcf:Operation>
        </bam:Expression>
      </bam:Update>
      <bam:Update DataItemName="State" Type="NVARCHAR">
        <bam:Expression>
          <bamwcf:Operation Name="XPath">
            <bamwcf:Argument>//*[local-name(.)='State']</bamwcf:Argument>
          </bamwcf:Operation>
        </bam:Expression>
      </bam:Update>
      <bam:Update DataItemName="Amount" Type="FLOAT">
        <bam:Expression>
          <bamwcf:Operation Name="XPath">
            <bamwcf:Argument>//*[local-name(.)='Amount']</bamwcf:Argument>
          </bamwcf:Operation>
        </bam:Expression>
      </bam:Update>
    </bam:OnEvent>
    <bam:OnEvent Name="EndOrder" IsBegin="false" IsEnd="true" Source="wcfSource">
      <bam:Filter>
        <bam:Expression>
          <bamwcf:Operation Name="GetServiceContractCallPoint">
          </bamwcf:Operation>
          <bam:Operation Name="Constant">
            <bam:Argument>ServiceReply</bam:Argument>
          </bam:Operation>
          <bam:Operation Name="Equals">
          </bam:Operation>
        </bam:Expression>
      </bam:Filter>
      <bam:CorrelationID>
        <bam:Expression>
          <bamwcf:Operation Name="XPath">
            <bamwcf:Argument>
              //*[local-name(.)='SubmitPoResult']
            </bamwcf:Argument>
          </bamwcf:Operation>
        </bam:Expression>
      </bam:CorrelationID>
      <bam:Update DataItemName="EndOrder" Type="DATETIME">
        <bam:Expression>
          <bamwcf:Operation Name="GetContextProperty">
            <bamwcf:Argument>EventTime</bamwcf:Argument>
          </bamwcf:Operation>
        </bam:Expression>
      </bam:Update>
    </bam:OnEvent>
  </bam:BamActivity>
</bam:InterceptorConfiguration>

posted @ 2008-05-26 13:44 montaque 阅读(1160) | 评论 (3)编辑

2008年5月24日