Hello,Biztalk 2006 R2 BAM, WCF 集成

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正常调用。

Hello,Biztalk 2006 R2 BAM, WCF 集成_第1张图片

 

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

Hello,Biztalk 2006 R2 BAM, WCF 集成_第2张图片

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

Hello,Biztalk 2006 R2 BAM, WCF 集成_第3张图片


 

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

Hello,Biztalk 2006 R2 BAM, WCF 集成_第4张图片

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

关于拦截模型。

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

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

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

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

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

Hello,Biztalk 2006 R2 BAM, WCF 集成_第5张图片

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

然后是一个事件的tree

Hello,Biztalk 2006 R2 BAM, WCF 集成_第6张图片

 

接下来我们配置一个我们的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的元数据库中。你可以查询

Hello,Biztalk 2006 R2 BAM, WCF 集成_第7张图片

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

 

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

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

Advance-> Extensions->new behavior element extension

Hello,Biztalk 2006 R2 BAM, WCF 集成_第8张图片

新建一个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 程序集。

Hello,Biztalk 2006 R2 BAM, WCF 集成_第9张图片

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

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

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

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

Hello,Biztalk 2006 R2 BAM, WCF 集成_第10张图片

此时完整的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 提供的数据。

Hello,Biztalk 2006 R2 BAM, WCF 集成_第11张图片

 

如何调试知道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

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

Hello,Biztalk 2006 R2 BAM, WCF 集成_第12张图片

这样。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>

你可能感兴趣的:(WCF)