简介
在这篇文章中,我曾试图把我的思维过程,为实施一个轻量级ESB(企业服务总线),利用WCF的4基于内容的路由功能和XSLT 2.0的消息转换能力。在这里,我们会处理多个合同,多路由服务,多个输入消息,和多个输出消息对应的输入消息。最后,我们还将看到如何去进一步完善实施,利用WCF的4 WS - Discovery协议支持功能块,Windows Server AppFabric中的新的孩子,在企业SOA的托管和管理WCF服务,实施方案中涉及的微软技术。背景
这篇文章要求先验知识的WCF,C#和NET 4.0。 ESB工具包2.0,BizTalk Server 2009中,和Windows Server AppFabric中的知识是可取的,但没有必要。之前SOA的知识也是不可取的。
企业SOA模型有五个水平层次和四个垂直层,共9层。层和所涉及的技术介绍如下:生产者01层:业务系统 - 他们是离散的应用和业务系统运行在一个企业,形成孤岛和数据,知识和交易历史的岛屿。02:业务组件层 - 他们是有关业务的不同的学科领域,实现分组。 ADO.NET实体框架,定制组件,(可能在COM交易的一部分)等,有可能实现的这一层。03层:企业服务 - 他们是松散耦合,自治区,直辖市,粗粒度的服务,露出下方的业务组件的功能。 WCF数据服务,WCF服务,ASP.NET Web服务,RESTful服务等,构成了这一层。 WCF是这里使用的技术。消费者04层:业务工作流程 - 这层实现业务编排,结合业务规则。使用Windows Workflow Foundation的4,这是BPEL标准的工作流实现。微软已经XLANGs这是BPEL4WS的兼容。 WRE的(工作流规则引擎),可用于实施业务规则存储。05层:客户端应用程序 - 这一层包括各种客户端应用程序。 Silverlight的4.0是这里的前沿技术。常见的层06层:企业服务总线 - ESB提供了一个新的功能范围,重点构建健壮的,连接的面向服务的应用纳入轻量级服务组合,动态解析端点和地图,Web服务和WS - *行程为基础的服务调用集成,故障管理和报告,并与第三方的SOA治理解决方案的集成。 Neudesic ESB是这一层的产品。该产品是由微软认可。07层:服务管理和监视 - 这一层主要是管理和监控服务。 Windows Server AppFabric中1.0版是这里的领先技术。08层:信息架构(服务元数据,注册表,BI) - 这一层集成与UDDI注册表UDDI(3.0)的服务,实现了数据架构等的SSIS(SQL Server集成服务),这里使用的是另一种技术。09层:SOA治理 - 这一层是用于SOA治理。有关服务和其他相关方面的治理政策,在这里实现。 AmberPoint公司的BizTalk纳米代理的一个产品是由微软SOA治理的赞同。 WCF的4还支持WS - Policy和WS - PolicyAttachment。使用代码
下面的东西都在这里给出的代码:两个产品服务暴露出来。两个客户服务暴露出来。选择任何两个客户服务取决于客户的邮件内容被称为一个路由服务公开。另一个路由服务公开选择两个产品服务的任何一个被称为取决于产品信息内容。两个XSLT转换:输入客户信息转化输出客户的详细消息,和其他输入产品信息转化输出成品消息。服务合同和相应的数据输入和输出消息,为客户和产品的合同。的服务,为客户和产品实施的合同。实施路由服务和其他独立服务的配置文件。
从上述的步骤,它是明确的,我们要实现的内容为基础的路由结合的消息转换。
以下步骤进行:创建一个空白的Visual Studio解决方案,并将它命名为LightWeightESB.sln。所谓CustomerProductContract添加一个类库。添加代码文件名为下列内容ICustomer.cs:using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
using System.Runtime.Serialization;
namespace CustomerProductContract
{
[ServiceContract]
public interface ICustomer
{
[OperationContract]
CustomerDetail GetCustomerDetails(Customer cust);
}
[DataContract]
public class Customer
{
[DataMember]
public string CustomerID { get; set; }
[DataMember]
public string CustomerName { get; set; }
[DataMember]
public string CustomerCreditRating { get; set; }
}
[DataContract]
public class CustomerDetail
{
[DataMember]
public string CustomerID { get; set; }
[DataMember]
public string CustomerFirstName { get; set; }
[DataMember]
public string CustomerMiddleName { get; set; }
[DataMember]
public string CustomerLastName { get; set; }
[DataMember]
public string CustomerCreditRating { get; set; }
}
}
Customer类对应的输入消息,并CustomerDetail类对应的输出消息。消息映射使用XSLT 2.0地图输入客户信息输出客户详细消息,实现。接下来,添加一个代码文件名为IProduct.cs包含以下代码:
在这里,产品类对应的输入消息和FinishedProduct类对应的输出消息。使用XSLT 2.0的输入和输出消息之间的映射。因此,我们看到,我们有多个输入收到的消息的轻量级服务总线。System.ServiceModel.dll,System.ServiceModel.Description.dll,System.ServiceModel.Web.dll,System.Runtime.Serialization.dll项目添加引用。添加包含以下代码的代码文件TransformUtility.cs:using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.XPath;
using System.Xml.Serialization;
using System.IO;
namespace CustomerProductContract
{
public static class TransformUtility
{
private static XmlSerializer GetSerializer(Type type)
{
return new XmlSerializer(type);
}
public static string ToXml<T>(T obj)
{
var xs = GetSerializer(typeof(T));
var sb = new StringBuilder();
using (var swriter = new StringWriter(sb))
{
xs.Serialize(swriter, obj);
swriter.Close();
}
return sb.ToString();
}
public static T FromXml<T>(string xml)
{
T axb;
var xs = GetSerializer(typeof(T));
using (var reader = new StringReader(xml))
{
axb = (T)xs.Deserialize(reader);
reader.Close();
}
return axb;
}
public static string TransformXml(TextReader reader, bool isCustomer)
{
//Change this path suitably as per location of your solution folder
string xslPath = isCustomer ? @"F:\Works\ProtocolBridge\Customer" +
@"Contract\MappingMapToCustomerDetails.xslt" :
@"F:\Works\ProtocolBridge\CustomerContract" +
@"\MappingMapToFinishedProducts.xslt";
XPathDocument xpathDoc = new XPathDocument(reader);
XslCompiledTransform xsl = new XslCompiledTransform();
xsl.Load(xslPath);
StringBuilder sb = new StringBuilder();
TextWriter tw = new StringWriter(sb);
xsl.Transform(xpathDoc, null, tw);
return sb.ToString();
}
}
}
现在,这需要一些解释。 ToXmllt; GT;()方法一个NET对象序列化为等价的XML字符串,并在FromXmllt; GT;()方法反序列化XML字符串为一个等价的。NET对象。泛型是用在这里,因为我们可以反序列化和序列化任何类型,使用上述方法。 DataContract属性装饰类会自动将它序列化。的TransformXML()方法需要输入的TextReader相当于一个XML消息转换使用XslCompiledTransform,利用XSLT文件包含转换。现在我们需要添加。XSLT文件。首先,我们将添加MappingMapToCustomerDetails.xslt的文件。右键单击该项目,并使用添加- GT,新项目的选项添加XSLT文件。复制以下内容:<stylesheet version="2.0" exclude-result-prefixes="xs fn"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<output method="xml" indent="yes" encoding="UTF-8"/>
<template match="/">
<customerdetail >
<attribute name="xsi:noNamespaceSchemaLocation"
namespace="http://www.w3.org/2001/XMLSchema-instance"/>
<for-each select="Customer">
<customerid>
<value-of select="CustomerID"/>
</customerid>
</for-each>
<for-each select="Customer">
<customermiddlename>
<value-of select="CustomerName"/>
</customermiddlename>
</for-each>
</customerdetail>
</template>
</stylesheet>
在这里,我们提供了一个客户和CustomerDetail对象序列化为XML字符串的客户对象,然后应用的转化,通过反序列化到一个CustomerDetail对象生成的XML字符串之间的映射。因此,我们实现输入消息的转型,输出消息,这是ESB的首要特征之一。接下来,我们添加了同样的XSLT文件,名称MappingMapToFinishedProducts.xslt,包含以下内容。<stylesheet version="2.0" exclude-result-prefixes="xs fn"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<output method="xml" indent="yes" encoding="UTF-8"/>
<template match="/">
<finishedproduct>
<attribute name="xsi:noNamespaceSchemaLocation"
namespace="http://www.w3.org/2001/XMLSchema-instance"/>
<for-each select="Product">
<productname>
<value-of select="ProductName"/>
</productname>
</for-each>
<for-each select="Product">
<productcategory >
<value-of select="ProductCategory"/>
</productcategory>
</for-each>
</finishedproduct>
</template>
</stylesheet>
在这里,我们提供了一个第一产品对象序列化为XML字符串和随后的转化应用的产品和FinishedProduct对象之间的映射,其次是反序列化到一个FinishedProduct对象中生成的XML字符串。我们正在做简单的转换,在双方的上述案件只是为了显示转型在ESB的概念,而不是深入钻研XSLT的特质。现在,编译和构建解决方案。接下来,添加一个WCF服务应用程序的解决方案,并将它命名为ExclusiveProductService。在ExclusiveProductService.svc文件的下列内容:
在代码隐藏文件ExclusiveProductService.svc.cs下面的代码:<%@ServiceHost Language="C#"
Debug="true"
Service="ExclusiveProductService.ExclusiveProductService"
CodeBehind="ExclusiveProductService.svc.cs"%>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.IO;
namespace ExclusiveProductService
{
// NOTE: You can use the "Rename" command
// on the "Refactor" menu to change the class name
// "Service1" in code, svc and config file together.
public class ExclusiveProductService : CustomerProductContract.IProduct
{
public CustomerProductContract.FinishedProduct
GetProductDetails(CustomerProductContract.Product prod)
{
string prodXml =
CustomerProductContract.TransformUtility.ToXml
<CustomerProductContract.Product>(prod);
StringReader reader = new StringReader(prodXml);
string finProdXml =
CustomerProductContract.TransformUtility.TransformXml(reader, false);
CustomerProductContract.FinishedProduct finProd =
CustomerProductContract.TransformUtility.FromXml
<CustomerProductContract.FinishedProduct>(finProdXml);
return finProd;
}
}
}
在这里,IProduct接口实现。我们首先得到相当于输入产品对象的XML然后采取读者对象内部,并随后致电TransformXML方法。获得反序列化输出对象后,我们返回它。在相应的web.config文件,把下面的代码:
接下来,我们添加另一个WCF服务应用程序名为GeneralProductService的解决方案。接下来,在GeneralProductService.svc文件中,我们添加以下代码:<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
</system.web>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_ICustomerProduct">
<security mode="None"/>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="ExclusiveProductService">
<endpoint address="ExclusiveProductService.svc"
contract="CustomerProductContract.IProduct"
bindingConfiguration="BasicHttpBinding_ICustomerProduct"
binding="basicHttpBinding"/>
<endpoint address="mex"
contract="IMetadataExchange"
binding="mexHttpBinding"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost/ExclusiveProductService/"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value
below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInfaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
在代码隐藏文件GeneralProductService.svc.cs下面的代码:<%@ServiceHost Language="C#" Debug="true"
Service="GeneralProductService.GeneralProductService"
CodeBehind="GeneralProductService.svc.cs"%>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Xml;
using System.IO;
namespace GeneralProductService
{
// NOTE: You can use the "Rename" command
// on the "Refactor" menu to change the class name
// "Service1" in code, svc and config file together.
public class GeneralProductService : CustomerProductContract.IProduct
{
public CustomerProductContract.FinishedProduct
GetProductDetails(CustomerProductContract.Product prod)
{
string prodXml =
CustomerProductContract.TransformUtility.ToXml
<CustomerProductContract.Product>(prod);
StringReader reader = new StringReader(prodXml);
string finProdXml =
CustomerProductContract.TransformUtility.TransformXml(reader, false);
CustomerProductContract.FinishedProduct finProd =
CustomerProductContract.TransformUtility.FromXml
<CustomerProductContract.FinishedProduct>(finProdXml);
return finProd;
}
}
}
在这里,我们实施IProduct接口。首先,我们获得通过输入产品的对象,并转换成一个XML字符串相当于。然后,我们转换到输出XML,反序列化,产生FinishedProduct对象,它的输出消息。
相应的web.config文件,应当具备下列条件:
现在添加另一个WCF服务应用程序名为OrdinaryCustomerService解决方案。相应的OrdinaryCustomerService.svc文件应该有下面的代码:<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
</system.web>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_ICustomerProduct"/>
<security mode="None"/>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="GeneralProductService"/>
<endpoint address="GeneralProductService.svc"
contract="CustomerProductContract.IProduct"
bindingConfiguration="BasicHttpBinding_ICustomerProduct"
binding="basicHttpBinding"/>
<endpoint address="mex" contract="IMetadataExchange"
binding="mexHttpBinding"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost/GeneralProductService/"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value
below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
和代码隐藏文件OrdinaryCustomerService.svc.cs,应包含以下代码:<%@ ServiceHost Language="C#" Debug="true"
Service="OrdinaryCustomerService.OrdinaryCustomerService"
CodeBehind="OrdinaryCustomerService.svc.cs" %>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.IO;
namespace OrdinaryCustomerService
{
// NOTE: You can use the "Rename" command on the "Refactor"
// menu to change the class name "Service1"
// in code, svc and config file together.
public class OrdinaryCustomerService : CustomerProductContract.ICustomer
{
public CustomerProductContract.CustomerDetail
GetCustomerDetails(CustomerProductContract.Customer cust)
{
string custXml = CustomerProductContract.TransformUtility.ToXml
<CustomerProductContract.Customer>(cust);
StringReader reader = new StringReader(custXml);
string detCustXml =
CustomerProductContract.TransformUtility.TransformXml(reader, true);
CustomerProductContract.CustomerDetail detCust =
CustomerProductContract.TransformUtility.FromXml
<CustomerProductContract.CustomerDetail>(detCustXml);
return detCust;
}
}
}
在这里,ICustomer合同的实施。 GetCustomerDetails需要一个Customer对象的方法,得到相同的XML字符串相当于,并转换成输出CustomerDetail对象返回。相应的web.config文件应包含以下代码:
接下来,添加另一个WCF服务应用程序的解决方案和它命名为PremiumCustomerService。相应的PremiumCustomerService.svc文件应包含以下代码:<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
</system.web>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_ICustomer">
<security mode="None"/>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="OrdinaryCustomerService">
<endpoint address="OrdinaryCustomerService.svc"
contract="CustomerProductContract.ICustomer"
bindingConfiguration="BasicHttpBinding_ICustomer"
binding="basicHttpBinding"/>
<endpoint address="mex"
contract="IMetadataExchange" binding="mexHttpBinding"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost/OrdinaryCustomerService/"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below
to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true" />
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before
deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
在代码隐藏文件PremiumCustomerService.svc.cs文件应包含:<%@ServiceHost Language="C#" Debug="true"
Service="PremiumCustomerService.PremiumCustomerService"
CodeBehind="PremiumCustomerService.svc.cs"%>
现在相应的web.config文件中应包含以下代码:using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.IO;
namespace PremiumCustomerService
{
// NOTE: You can use the "Rename" command on
// the "Refactor" menu to change the class name
// "Service1" in code, svc and config file together.
public class PremiumCustomerService : CustomerProductContract.ICustomer
{
public CustomerProductContract.CustomerDetail
GetCustomerDetails(CustomerProductContract.Customer cust)
{
string custXml = CustomerProductContract.TransformUtility.ToXml
<CustomerProductContract.Customer>(cust);
StringReader reader = new StringReader(custXml);
string detCustXml =
CustomerProductContract.TransformUtility.TransformXml(reader, true);
CustomerProductContract.CustomerDetail detCust =
CustomerProductContract.TransformUtility.FromXml
<CustomerProductContract.CustomerDetail>(detCustXml);
return detCust;
}
}
}
现在我们将添加的路由服务。首先,我们将添加一个WCF服务应用程序名为ProductService。这项服务路由产品信息的服务之一,无论是GeneralProductService或ExsclusiveProductService,取决于ProductCategory属性的内容。ProductService.svc文件应包含以下代码:<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
</system.web>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_ICustomer">
<security mode="None"/>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="PremiumCustomerService">
<endpoint address="PremiumCustomerService.svc"
contract="CustomerProductContract.ICustomer"
bindingConfiguration="BasicHttpBinding_ICustomer"
binding="basicHttpBinding"/>
<endpoint address="mex"
contract="IMetadataExchange"
binding="mexHttpBinding"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost/PremiumCustomerService/"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value
below to false and remove the metadata endpoint
above before deployment -->
<serviceMetadata httpGetEnabled="true" />
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
<%@ ServiceHost Language="C#" Debug="true"
Service="System.ServiceModel.Routing.RoutingService,System.ServiceModel.Routing,
version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
在这里,我们正在使用的服务驻留在System.ServiceModel.Routing.dll System.ServiceModel.Routing.RoutingService。不用说,现在我们需要添加一个引用System.ServiceModel.Routing.dll项目。此路由服务的web.config文件应包含以下代码:<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
</system.web>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_ICustomerProduct">
<security mode="None"/>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint name="ProductServiceLibrary_GeneralProductService"
address="http://localhost/GeneralProductService/GeneralProductService.svc"
contract="*"
bindingConfiguration="BasicHttpBinding_ICustomerProduct"
binding="basicHttpBinding"/>
<endpoint name="ProductServiceLibrary_ExclusiveProductService"
address="http://localhost/ExclusiveProductService/ExclusiveProductService.svc"
contract="*" bindingConfiguration="BasicHttpBinding_ICustomerProduct"
binding="basicHttpBinding"/>
<endpoint address="mex" contract="IMetadataExchange"
binding="mexHttpBinding"/>
</client>
<services>
<service name="System.ServiceModel.Routing.RoutingService"
behaviorConfiguration="RoutingServiceBehavior"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost/ProductService/"/>
</baseAddresses>
</host>
<endpoint name="RoutingServiceEndpoint"
contract="System.ServiceModel.Routing.IRequestReplyRouter"
bindingConfiguration="BasicHttpBinding_ICustomerProduct"
binding="basicHttpBinding"/>
<endpoint name="udpDiscovery" kind="udpDiscoveryEndpoint"/>
<endpoint address="mex" contract="IMetadataExchange"
binding="mexHttpBinding"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="RoutingServiceBehavior">
<!-- To avoid disclosing metadata information, set the value below
to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpgetenabled="true" />
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
<routing routeOnHeadersOnly="False"
filterTableName="routingRules">
<serviceDiscovery/>
</behavior>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<routing>
<namespaceTable>
<add namespace="http://schemas.datacontract.org/2004/07/CustomerProductContract"
prefix="pp"/>
</namespaceTable>
<filters>
<filter name="GeneralProductFilter"
filterData="//pp:ProductCategory = 'General'"
filterType="XPath"/>
<filter name="ExclusiveProductFilter"
filterData="//pp:ProductCategory = 'Exclusive'"
filterType="XPath"/>
</filters/>
<filterTables>
<filterTable name="routingRules">
<add priority="0"
endpointName="ProductServiceLibrary_GeneralProductService"
filterName="GeneralProductFilter"/>
<add priority="0"
endpointName="ProductServiceLibrary_ExclusiveProductService"
filterName="ExclusiveProductFilter"/>
</filterTable>
</filterTables>
<backupLists>
<backupList name="ProductBackupList">
<add endpointName="ProductServiceLibrary_GeneralProductService"/>
</backupList>
</backupLists>
</routing>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
在这里,在serviceBehaviors一节中,我们指定的路由元素filterTableName属性。我们还使serviceDiscovery这里。在路由部分,我们在XPath表达式中使用基于路由的内容添加一个命名空间前缀。我们添加一个过滤器在过滤器部分。器filtertype我们指定XPath和筛选数据,我们指定实际的XPath表达式,使用以前声明的命名空间前缀。该过滤器是什么DataContract属性装饰类的公共的自动属性,但值。我们还宣布从失败中软恢复一个backupLists部分。接下来,我们添加另一个WCF服务应用程序解决方案,命名它为客户服务。CustomerService.svc文件应包含以下代码:<%@ServiceHost Language="C#" Debug="true"
Service="System.ServiceModel.Routing.RoutingService,System.ServiceModel.Routing,
version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"%>
我们可以看到,每个接口创建一个路由服务需求。此路由服务的web.config文件中的对应应该包含下面的代码:<configuration>
<system.web>
<compilation debug="true" targetframework="4.0"/>
</system.web>
<system.servicemodel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_ICustomer">
<security mode="None"/>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint name="CustomerServiceLibrary_PremiumCustomerService"
address="http://localhost/PremiumCustomerService/PremiumCustomerService.svc"
contract="*" bindingConfiguration="BasicHttpBinding_ICustomer"
binding="basicHttpBinding"/>
<endpoint name="CustomerServiceLibrary_OrdinaryCustomerService"
address="http://localhost/OrdinaryCustomerService/OrdinaryCustomerService.svc"
contract="*" bindingConfiguration="BasicHttpBinding_ICustomer"
binding="basicHttpBinding"/>
<endpoint address="mex"
contract="IMetadataExchange" binding="mexHttpBinding"/>
</client>
<services>
<service name="System.ServiceModel.Routing.RoutingService"
behaviorConfiguration="RoutingServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost/CustomerService/"/>
</baseAddresses>
</host>
<endpoint name="RoutingServiceEndpoint"
contract="System.ServiceModel.Routing.IRequestReplyRouter"
bindingConfiguration="BasicHttpBinding_ICustomer"
binding="basicHttpBinding"/>
<endpoint name="udpDiscovery" kind="udpDiscoveryEndpoint"/>
<endpoint address="mex"
contract="IMetadataExchange" binding="mexHttpBinding"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="RoutingServiceBehavior">
<!-- To avoid disclosing metadata information, set the value below
to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true" />
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
<routing routeOnHeadersOnly="False"
filterTableName="routingRules"/>
<serviceDiscovery/>
</behavior>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<routing>
<namespaceTable>
<add namespace="http://schemas.datacontract.org/2004/07/CustomerProductContract"
prefix="cc"/>
</namespaceTable>
<filters>
<filter name="PremiumCustomerFilter"
filterData="//cc:CustomerCreditRating = 'Good'"
filterType="XPath"/>
<filter name="OrdinaryCustomerFilter"
filterData="//cc:CustomerCreditRating = 'Bad'"
filterType="XPath"/>
</filters>
<filterTables>
<filterTable name="routingRules">
<add priority="0"
endpointName="CustomerServiceLibrary_PremiumCustomerService"
filterName="PremiumCustomerFilter"/>
<add priority="0"
endpointName="CustomerServiceLibrary_OrdinaryCustomerService"
filterName="OrdinaryCustomerFilter"/>
</filterTable>
</filterTables>
<backupLists>
<backupList name="CustomerBackupList">
<add endpointName="CustomerServiceLibrary_OrdinaryCustomerService" />
</backupList>
</backupLists>
</routing>
</system.serviceModel>
<system.webserver>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
此外,我们宣布路由使用路由部分的逻辑,实施filterTable一个XPath表达式,其中包含对筛选数据的路由过滤器的基础上CustomerCreditRating公共Customer类的自动属性。现在,我们需要思考托管。我们可以部署Windows Server AppFabric中的所有服务。事实上,任何服务部署到IIS 7.0将拿起的AppFabric管理工具。我们只需要确保应用服务器扩展。NET 4中已预先安装在部署之前。我们通过建立相应的虚拟目录对应的物理目录,如客户服务,ExclusiveProductService,GeneralProductService,OrdinaryCustomerService,PremiumCustomerService,并ProductService部署的所有服务。现在我们需要添加客户端项目。添加Windows窗体应用程序的解决方案,并将它命名为ProtocolBridgingClient。app.config文件中不包含任何超过以下:
重命名Form1.cs的ProtocolBridgingForm.cs,这个Windows窗体的代码应该包含以下内容:<configuration>
<system.serviceModel>
<client />
</system.serviceModel>
</configuration>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Discovery;
namespace ProtocolBridgingClient
{
public partial class ProtocolBridgingForm : Form
{
public ProtocolBridgingForm()
{
InitializeComponent();
}
private void ProtocolBridgingForm_Load(object sender, EventArgs e)
{
try
{
BasicHttpBinding binding = new BasicHttpBinding();
EndpointAddress custAddress = new EndpointAddress(
"http://localhost/CustomerService/CustomerService.svc");
CustomerProductContract.ICustomer custProxy =
ChannelFactory<CustomerProductContract.ICustomer>.CreateChannel(
binding, custAddress);
CustomerProductContract.Customer cust1 =
new CustomerProductContract.Customer
{
CustomerID = "ar0045855",
CustomerName = "Ambar Ray",
CustomerCreditRating = "Good"
};
CustomerProductContract.Customer cust2 =
new CustomerProductContract.Customer
{
CustomerID = "am0046067",
CustomerName = "Abhijit Mahato",
CustomerCreditRating = "Bad"
};
EndpointAddress prodAddress = new EndpointAddress(
"http://localhost/ProductService/ProductService.svc");
CustomerProductContract.IProduct prodProxy =
ChannelFactory<CustomerProductContract.IProduct>.CreateChannel(
binding, prodAddress);
CustomerProductContract.Product prod1 =
new CustomerProductContract.Product { ProductName = "LUX",
ProductCategory = "General" };
CustomerProductContract.Product prod2 =
new CustomerProductContract.Product { ProductName = "VIM",
ProductCategory = "Exclusive" };
CustomerProductContract.CustomerDetail custDet1 =
custProxy.GetCustomerDetails(cust1);
CustomerProductContract.CustomerDetail custDet2 =
custProxy.GetCustomerDetails(cust2);
CustomerProductContract.FinishedProduct finProd1 =
prodProxy.GetProductDetails(prod1);
CustomerProductContract.FinishedProduct finProd2 =
prodProxy.GetProductDetails(prod2);
string res1 = custDet1.CustomerID + " " +
custDet1.CustomerFirstName + " " +
custDet1.CustomerMiddleName +
" " + custDet1.CustomerLastName +
" " + custDet1.CustomerCreditRating;
string res2 = custDet2.CustomerID + " " +
custDet2.CustomerFirstName +
" " + custDet2.CustomerMiddleName + " " +
custDet2.CustomerLastName + " " +
custDet2.CustomerCreditRating;
string res3 = finProd1.BatchID + " " + finProd1.MfgDate + " " +
finProd1.ExpDate + " " + finProd1.ProductName +
" " + finProd1.ProductName;
string res4 = finProd2.BatchID + " " + finProd2.MfgDate +
" " + finProd2.ExpDate + " " +
finProd2.ProductName + " " + finProd2.ProductName;
txtCustomer.Text = res1 + "\n" + res2 +
"\n" + res3 + "\n" + res4;
}
catch (Exception ex)
{
txtCustomer.Text = (ex.InnerException != null) ?
ex.InnerException.Message + "\t" +
ex.Message : ex.Message;
}
}
private void ProtocolBridgingForm_FormClosed(object sender,
FormClosedEventArgs e)
{
GC.Collect();
}
}
}
在这里,我们创建代理类为客户服务(路由服务)和使用的ChannelFactorylt ProductService(路由服务); GT; CreateChannel()方法,basicHttpBinding的EndpointAddress对象作为参数传递。 EndpointAddress对象创建路由服务的硬编码的URI。最后,我们调用代理的方法,这反过来又调用服务。结论
最后,我们所做的是,使用一个路由服务,我们是客户信息路由到两个客户的相关服务取决于消息的内容之一,同样,我们使用另一个路由服务路由产品信息取决于消息的内容与产品相关的服务之一。此外,在得到消息后,我们正在改变使用XSLT转换的消息,然后发回输出消息。因此,我们正在实现路由和转换。兴趣点
客户端代码中,我们有硬编码为路由服务路由服务的URI。这可避免使用的WS - Discovery协议支持功能的WCF 4。我们可以说的WS - Discovery是一个基于UDP的多播消息交换。消息接收服务的终点信息,并使用这一发现信息。客户端使用发现信息,发现在网络上提供服务。此前,我们在web.config文件中相应的路由服务启用支持服务发现通过增加一个额外的端点服务,如:...
<endpoint name="udpDiscovery" kind="udpDiscoveryEndpoint"/>
...
因此,我们做这项服务动态服务。现在,在客户端,我们需要添加一个System.ServiceModel.Discovery.dll引用。在客户端,我们可以添加代码如下:...
DiscoveryClient discoverclient =
new DiscoveryClient(new UdpDiscoveryEndpoint());
FindResponse response = discoverclient.Find(
new FindCriteria(typeof(CustomerProductContract.ICustomer)));
EndpointAddress address = response.Endpoints[0].Address;
...
此外,我们可以使用UDDI 3.0 API的整合与UDDI 3.0注册表,运行时确定服务端点(动态路由),可以做到WCF服务。我离开它是在这篇文章中,因为它需要有更多的增强,才能真正作为一种低成本,轻量级ESB的微软平台的替代。我期待着一个定制的轻量级ESB的解决方案在未来的很多即兴形式。现在,使用ESB工具包2.0使用BizTalk Server,再次任务使用SQL Server的任务。一个ESB替代基于WCF 4.0和XSLT 2.0将提供一个低成本的替代产品,快速的投资回报,随着建筑师使用多个数据库产品,而不是仅仅坚持到SQL Server的灵活性。历史2004年8月04 - 发表。