返回首页

目录{A}松耦合的设计模式无需编码WorkflowService虚拟化模型驱动设计WCF的连接WF的业务流程(类型化和非类型化的消息)内置自举/ Loader服务基于业务的AppDomain的托管服务WSDL,XOML,规则和XSLT配置的元数据,存储在仓库同步和异步连接时尚发布/订阅功能 基于XSLT的metadada消息中介内置WS - Resource的传输调解员功能插件到企业服务总线WCF和WF的可扩展性支持IIS7/WAS和自我托管的。NET FX 3.5技术
微软最近发布了一个版本。NET FX 3.5,他们推出一个新的上下文驱动的服务模式,呼吁WorkflowService。这个模型是基于的Windows Communication Foundation(WCF)和Windows工作流基础(WF)技术。它代表了连接揭露工作流逻辑模型。服务工作流程中的操作是抽象成新的上下文驱动的活动,如合同或工作流程的第一宣言为基础的ReceiveActivity。
业务连接由客户端(通道)或新的上下文驱动的活动(SendActivity)表示层可以同步或异步的方式(MEP)的消息交换模式的一个变种一样使用另一个标准的服务工作流服务,透明输入/输出,请求/应答,会话,双面等,对不同的物理传输,如TCP,HTTP,MSMG,管等,是一个伟大的开放式连接模式,定制和扩展能力的基础上的所有元素业务需求。
除此之外,WorkflowService使协调服务操作。这一共同声明的基础上的元数据,使用模型的第一个工具,可以创建服务的连接和​​操作的逻辑模型。这是一个伟大的功能,工作流服务模型,元数据存储在知识库中相应的服务和工作流程(称为资源库)时,允许创建和发起的连接和运行在特定的AppDomain。
让我们继续在我们的服务抽象。正如我前面提到的,服务业务为代表的顺序或状态机工作流模型。从连接点的观点,WCF模型所描述的元数据的连接,如地址,绑定合同(ABC)和行为。 WF模型所描述的工作流定义的XOML元数据或CLR类型声明。
封装的连接和消息中介进入前处理,处理一个实际的业务服务操作和后处理活动,WorkflowService可以代表一个VirtualService调解松散耦合的方式消耗的业务层透明。
下图显示了连接到ESB WorkflowService:
{S0}
WorkflowService,如上面的图片显示,是代表在申报的WCF和WF模型的元数据存储资源。工作流激活的XOML /预/后处理,并调用实际业务服务规则的元数据。
注意,工作流业务流程的重点是虚拟化的服务操作作为一个逻辑模型的一部分​​。其业务逻辑驱动器的一体化进程。此外,有许多种类的业务流程,基于消息中介,调度,重路由,虚拟化业务等的XOML和规则部分由设计时创建的元数据存储在知识库(服务存储库),和他们不'T编排一个实际的业务流程,只是前处理和后处理,以及如何调用业务流程。这是战略和驱动模型和元数据所代表的VirtualService概念。
所以,现在我们知道什么是落后的WorkflowService模式实施的VirtualService。下图显示了其在这篇文章中使用的符号。注意,是在应用程序的VirtualServices没有限制,它是独立的商业模式。

VirtualService生活在AppDomain中,它可以访问业务对象,服务和工作流程。下图为在位于相同的AppDomain内的私人业务层的顶部逻辑层VirtualServices的例子:
{S2}
当然,有没有VirtualService和业务层,服务,或个别服务业务之间的边界限制。 VirtualService可以组成(虚拟化)基础上的商业模式需要不同的服务业务。
下图显示了这个例子,其中一个前端层发布位于内部防火墙后面的民营企业服务的一部分:
{S3}
注意,在VirtualService不需要建立和重新编译解决方案中的的变化,因为它的模型是基于元数据,和托管预建的引导服务代理。上面的图片显示了一个虚拟化服务业务的私人商业服务(WCF中,Web服务,远程等)。
在设计时,建模工具将创建一个基于VirtualService响应返回给客户端元数据的一部分私人服务的WSDL / MEX文件。模型驱动架构是位于资源库的元数据在逻辑上集中的企业解决方案,和物理上分散的基础上部署模式。建模是一种在整个企业网络的应用的设计,模拟和部署的重要组成部分。如果没有合适的工具,将很难处理和管理更加复杂的解决方案,例如,端点,消息中介,XOML,规则,等等。
行,托管在AppDomain VirtualService回。商业模式映射到物理模型,让我们到AppDomain的代表物理层逻辑业务组和隔离。这种模式的策略是非常重要的,在企业架构的可靠性和可管理性的目的。
下图显示了一个独立的业务部门的AppDomain中的应用过程:
{S4}的
上面的图片显示,应用过程中会自动创建一个默认的AppDomain与一个特殊的内置ESB服务代理。这个代理有责任创建和管理一个特定的AppDomain为托管元数据为基础的VirtualServices。请注意,造型,将创造一切必要的元数据运行时引导/装载机AppDomain的名称,标识等,如行动
这是一个伟大的VirtualService功能,模型可以决定其托管业务需求的基础上。 VirtualServices业务的AppDomain可以加载和重新飞在特定的顺序,而不中断其他非相关业务。ESB和VirtualService
消耗一个VirtualService是完全透明的,由元数据抽象,它宣布quot;谁和howquot;可以谈服务和操作。这是一种编程风格不同的方法,被称为声明性编程,其中分为预先建立的通用基础设施组件,元数据驱动的解决方案是,允许在逻辑模型管理的解决方案。
下面的图片显示之间的逻辑连接的服务和消费者使用管理的基础设施 - 企业服务总线:
{五}
正如你可以看到上面的图片中,企业服务总线,代表的业务层之间的逻辑连接的执行情况。商业模式映射到物理之一是生产工具,在设计/建模时间。建模的结果是存储在库中的元数据。要了解此元数据在运行时,ESB提供了预先建立的通用基础设施,如适配器,连接器,引导/装载机等(组件)
下面的图片显示模型驱动的ESB战略:
{中六}
在部署时间,部署元数据描述的商业模式,在企业服务库(ESR)是基于部署模式LocalRepository。注意,LocalRepository引导/装载机服务的实时元数据缓存:
{七}
企业服务储存库中包含的企业解决方案的逻辑模型,其中VirtualServices在逻辑上是通过连接器连接到企业服务总线。连接器是代表在作为一个资源的VirtualService元数据库。
下面的图片显示了作为一个业务服务连接到ESB的连接器VirtualService的逻辑位置:
{S8}
正如你可以看到,VirtualService是为业务服务的中介。如XOML,规则,XSLT等,VirtualService是推动调解的消息,并转发到下一个目的地,如私人(或公共)服务或ESB元数据。下图显示了这样一个场景,VirtualService有一个嵌入适配器到ESB:

当然,VirtualService也可以被用于消息中介,像下面的图片所示:

这是所有描述VirtualService和ESB模型的元数据的位置。让我们回到我们的VirtualService。
正如我前面提到的,每个VirtualService必须宣布在仓库前使用引导/装载机。有两种可能级别的仓库。有地方和企业的水平。本地资源库中保存元数据映射为每个应用程序的主题的物理部署模式。这个元数据是要求在运行时应用程序的引导启动服务。本地资源库,可以管理使用MMC控制台的工具在物理水平。
下面的图片显示了作为一个为民营企业服务的门面服务的VirtualService的位置:
{S11}
本地资源库,可以存储在应用程序装配中,配置文件,文件系统或数据库。利用本地资源库,使我们能够使用VirtualServices私人中央应用程序配置的一部分。
下面的图片展示了一个配置文件LocalRepository节描述的元数据为引导/装载机的例子:
{S12}
正如你可以看到,有一个endpointName属性在LocalRepository部分元数据远程查询。基本上,VirtualService也可以在本地配置没有任何考虑内使用ESB模型。当然,在这种情况下,VirtualService是单独管理的具体应用程序内。另一方面,在本地资源库ESB.DomainLoader将使我们的应用程序由VirtualServices主办任何时间在一个完全透明的的方式。VirtualService调解
VirtualService的概念基础上的WorkflowService模型。基本上,有两个地方的服务中介。第一个是基于WCF的使用绑定,服务行为,操作,消息,和参数检查的自定义扩展的可扩展性。这种调解是通过注入一个强类型的调解员的输入和/或输出管道。该插件在这些调解员的地方是system.serviceModel部分配置文件。可以使用微软的SvcConfigEditor工具来处理这个部分。
第二位的是服务/运作的中介的XOML工作流程。这调解打开VirtualService的许多功能,此外,我们已经有一个工具,以协调的工作流程和规则产生的元数据(XOML,规则)。有没有在VirtualService的XOML工作流程的限制,它可以完全是一个商业服务解决方案,但是从设计模式的看法,这XOML应该处理前/调用/后处理没有实际的商业处理器。此基础上,我们可以说,VirtualService XOML负责调解的操作(消息交换模式),并从ESB的水暖业务处理器封装。
下面的图片显示为代表的自定义活动,如Transformer_IN和_OUT消息中介业务处理器(位于一个单独的顺序工作流)封装:
{S13}
下图为XOML VirtualService元数据的一部分的一个例子。
{S14}消息交换模式调解
在VirtualService使用新的ReceiveActivity,使抽象的服务操作中VirtualService。这个复合活动,允许指定的消息交换模式的基础上处理传入的服务消息。例如,请求/应答模式将请求返回给调用者返回一个值,对面的单向输出模式。
下面的图片展示了如何使用的情况下调用者需要收到确认返回值只MEP,是可以改变的,但实际的业务流程后,将在发布/订阅的方式处理:
现在,让我们的工作重点VirtualService经营合同。 WCF模型的基础上,VirtualService可以策划服务操作与类型化或非类型化的消息。类型的操作
与类型化的消息操作的合同,可以接收和处理直接操作的参数在方法签名。这种类型的操作是适合知名的服务业务,如WS - *(WS -事物,WS - ResourceTransfer等)的预建服务。 VirtualService合同是映射的XOML工作流使用的基础上的操作数的EventDrivenActivity分行的ListenActivity。
下面的图片展示的XOML两个经营合同的例子。 EventDrivenActivity的每个分支都有一个具体操作合同的ReceiveActivity。
{S16}
为WS - ResourceTransfer XOML的一个例子:

正如你可以看到在上面的例子,VirtualService可以处理四个业务的WS_Transfer合同。每个操作都有其自己的自定义复合活动处理的消息中介(看到内的ReceiveActivity彩色活动)和业务处理器调用。
又如,ESB的驱动WS - ResourceTransfer合同上的存储资源VirtualService:
非类型化的操作
与非类型化的消息的操作合同,允许更多的灵活性,VirtualService虚拟化不是一个类型的操作。在VirtualService使用非类型化的操作的概念的基础上创建一个通用的合同,这将是任何类型化和非类型化的消费,在一个完全透明的的方式有效。换句话说,我们要通过原始的信息建模的基础上其加工的ReceiveActivity。
下面的代码片段是与非类型化的操作的服务合同的例子:

[ServiceContract]

public interface IGenericContract

{

    [OperationContract(Action="*", ReplyAction="*")] 

    [TransactionFlow(TransactionFlowOption.Allowed)]

    Message ProcessMessage(Message message);

}

的ReceiveActivity接收传入的原始消息,可以选择的基础上正确的分支行动头,在以下的XOML模板例子所示:

非类型化的操作让我们在现有的服务创建一个不同的操作的复合服务(WSDL)的能力,配售消息的调解员,不断变化的消息交换模式等服务中介可以做结果在建模时间为引导/装载机服务的元数据。
下图显示了一个例子是XOML,验证传入的原始消息在政策活动,那么,行动值的基础上,通过特定的分支进行处理。有许多不同的可能的行动,包括一个操作/墨西哥返回一个WSDL文档,由一个非类型化的消息驱动这VirtualService;见下面的例子:
VirtualService与一个XOML的StateMachine
基本上,VirtualServices宣布的XOML顺序工作流(见所有的例子),但没有任何限制以及使用状态机工作流定义。在这种情况下,每个国家会收到这样的顺序工作流事件消息(类型化或非类型化)。在VirtualService使用一个状态机的优点是简化事件驱动的控制过程中,长​​期运行的业务流程,其中一个范围内的业务谈话需要通过更多的国家,例如,流:秩序,验证,付款,送货等
下面的图片显示VirtualService,服务中介是由国家机器策划的一个例子:
WCF服务(没有XOML)
正如我刚才所说,VirtualService是基于WorkflowService模型。在某些情况下(路由器,调度员,出版商,...), WCF服务模型也可以使用虚拟化的经营合同。在这种情况下,该消息只能介导对服务扩展组件的XOML工作流的灵活性。
下图为基于WCF服务VirtualService:

这是所有引进的企业服务总线和本地使用的范围VirtualService。
我假设你已经WCF和WF技术的知识和他们的可扩展性,因此,我将重点放在引导VirtualServices,这是只有一小部分的设计和实施,但一个很基本的组成部分ESB的设计和实施。
OK,让我们开始的设计,实施后续行动。
VirtualService的概念是基于一个元数据驱动的模型。松散耦合的WCF / WF配置模型是代表在s​​ystem.serviceModel节组,只允许使用在应用程序配置文件中存储的数据。有没有直接的。NET FX的内置支持来配置,如数据库,文件系统元数据的另一个来源等
的设计和实施VirtualService,需要解决的WCF / WF模型上下面层:服务的元数据结构(WSDL,XOML,规则,XSLT,端点,绑定,行为,工作流程,扩展,... ...)(托管元数据结构的过程中,AppDomain中,... ...)基于元数据的托管服务和WorkflowServices引导过程(冷启动)动态装卸服务,在具体业务的AppDomain发布/订阅服务(热启动) - 可选
这一层可以作为一个资源上的任何地方被称为企业服务储存库的企业网络存储的元数据的虚拟化的服务。本文的重点是在该层,这将允许在松散耦合的WCF / WF的可扩展模型为基础的方式,如插入一个消息检查,参数验证程序等,建立更多的复合服务
所附的文章实现允许使用这一解决方案在独立的和/或企业应用程序。下面的代码片段将只显示执行的显著部分。这是本文的范围来形容有关的企业服务总线解决方案的设计和实施的更多细节。我认为,VirtualService可以是一个良好的开端了组件移动作为一个元数据驱动的模型的ESB架构的思想。
确定,让我们继续前进VirtualService元数据描述:元数据
建立一个元数据驱动的体系结构模型,需要不同的编程方法。有关配置数据与元数据论坛的讨论会仍在继续。是从配置数据模型驱动的临界点在哪里?我们如何才能管理数据,我们有合适的工具,建模元数据等这些问题的答案(模型驱动的应用程序)是这篇文章中,配置数据(引导端点)是指向元数据;其他也就是说,在特定的应用范围,引导数据是对的应用程序元数据数据代表在上下文驱动的业务模式进行管理控制流的配置数据。当然,如果认为的范围(企业应用),也可以引导配置数据元数据的一部分。
所以,下面的图片显示了对VirtualService元数据的类图(合同):

在这个设计中,元数据资源代表身体的XML格式的文本或标识资源资源,因此属性的类型是一个字符串。正如我前面提到的,可以有两种ESB中的元数据,这样的设计是基于逻辑模型(逻辑元数据)的工具所产生的物理元数据。引导/装载机服务是本地的物理元数据处理器,其职责是建立和启动一个具体的AppDomain的服务(包括创建域,如果它不存在)。
以下资源的VirtualService要求:配置
配置元数据为代表的system.ServiceModel部分,就像它是用于在应用程序配置文件。请注意,此配置应只涉及一个特定的服务。 Microsoft服务配置编辑器工具,可以创建资源(XML格式的文本)的内容。默认值为空字符串使用应用程序配置文件资源。
配置元数据是放置插件的行为和有约束力的端点,工作流运行时服务,以及任何自定义对象的扩展。XOML和规则
XOML和规则的元数据需要通过工作流模型在VirtualService。 XOML代表一个由数据驱动的具体规则的经营合同的过程。此资源的XML格式的文本所产生的工作流设计建模过程中的一部分(在Visual Studio /永丰的一部分)。默认的值是一个没有工作流VirtualService或激活工作流类型空字符串。WSDL
这是一个用于描述服务的元数据标准文件。默认值是一个空字符串生成WSDL文档的VirtualService。在这种情况下,VirtualService是复利(虚拟化)服务,根据不同的服务操作,建模工具,有责任创造本文件作为服务元数据的一部分。XSLT
的XSLT元数据需要服务的调解员(调解)的输入/输出/错误信息,以适当的格式转换。在这个设计中,创建自定义ServiceMediators部分已申报System.Xml.Xsl.XslCompiledTransform类的调停要求的数据。默认值为空字符串没有要求调解。请注意,操作调解是一个非常强大的功能,参数和自定义功能,可以从服务上运行时转换通过的VirtualService。
下面的例子显示了serviceMediators部分:{C}
的所有设计的第一部分; VirtualService的元数据。下一步是要找到ServiceHost和WorkflowServiceHost可以驱动直接资源或间接从文件系统。实施简单的方法是重写ApplyConfiguration方法。重写ApplyConfiguration
下面的代码片段显示了此覆盖的实施,处理服务的配置是基于XML格式的文本,配置文件是在任何文件系统文件夹位于。
protected override void ApplyConfiguration()

{

  string config = (string)CallContext.GetData("_config");

  string configurationName = this.Description.ConfigurationName;



  if (config != null && config.TrimStart().StartsWith("<"))

  {

    ServiceModelSection model = 

      ServiceModelConfigHelper.DeserializeSection<ServiceModelSection>(config);

          

    // validate model

    // ...

        

    // add behavior based on the config metadata

    ServiceModelConfigHelper.AddBehaviors(this, model);



    ServiceElement se = model.Services.Services[configurationName];

    base.LoadConfigurationSection(se);



    // add custom binding based on the config metadata

    ServiceModelConfigHelper.AddCustomBinding(this, model);



    return;

  }

  else

  {

    ServiceElement se = 

      ServiceModelConfigHelper.GetServiceElement(configurationName, config);

    base.LoadConfigurationSection(se);

  }             

}

注意,基础虚拟ApplyConfiguration方法是在构造函数中调用,因此配置的元数据是通过通过CallContext线程插槽。正如你可以看到,覆盖的实施是短期和直接使用ServiceModelConfigHelper工具system.ServiceModel组的基础上,创建自己的ServiceModelSection的。
让我们来看看这些部件:ServiceModelSection
ServiceModelSection对象,使我们能够反序列化使用XmlReader一节。下面的代码片段显示了其实施情况,通过反射进行反序列化,其中每个元素的system.ServiceModel:
public sealed class ServiceModelSection : ConfigurationSection

{

  public ServicesSection Services { get; set; }

  public ClientSection Client { get; set; }

  public BehaviorsSection Behaviors { get; set; }

  public BindingsSection Bindings { get; set; }

  public ExtensionsSection Extensions { get; set; }

  public DiagnosticSection Diagnostics { get; set; }

    

  protected override void DeserializeSection(XmlReader reader)

  {

    Type cfgType = typeof(ConfigurationSection);

    MethodInfo mi = cfgType.GetMethod("DeserializeElement",

          BindingFlags.Instance | BindingFlags.NonPublic);



    reader.ReadStartElement("configuration");

    while (reader.NodeType != XmlNodeType.EndElement)

    {

       if (reader.Name == "system.serviceModel")

       {

         reader.ReadStartElement();

         while (reader.NodeType != XmlNodeType.EndElement)

         {

           #region sections

           if (reader.IsEmptyElement || 

              reader.NodeType == XmlNodeType.Whitespace)

           {

              reader.Skip();

              continue;

           }



           if (reader.Name == "diagnostics")

           {

             Diagnostics = new DiagnosticSection();

             mi.Invoke(Diagnostics, new object[]{reader, false});

             reader.ReadEndElement();

           }

           else if (reader.Name == "extensions")

           {

             Extensions = new ExtensionsSection();

             mi.Invoke(Extensions, new object[]{reader,false});

             reader.ReadEndElement();

           }

           else if (reader.Name == "services")

           {

             Services = new ServicesSection();

             mi.Invoke(Services, new object[]{reader,false});

             reader.ReadEndElement();

           }

           else if (reader.Name == "bindings")

           {

             Bindings = new BindingsSection();

             mi.Invoke(Bindings, new object[]{reader,false});

             reader.ReadEndElement();

           }

           else if (reader.Name == "behaviors")

           {

             Behaviors = new BehaviorsSection();

             mi.Invoke(Behaviors, new object[]{reader,false});

             reader.ReadEndElement();

           }

           else if (reader.Name == "client")

           {

             Client = new ClientSection();

             mi.Invoke(Client, new object[] { reader, false });

             reader.ReadEndElement();

           }

           #endregion

           reader.MoveToContent();

          }

        }

        reader.Skip();

        reader.MoveToContent();

        }

        reader.ReadEndElement();

    }

}
ServiceModelConfigHelper
ServiceModelConfigHelper是一个静态辅助类来隐藏一些私营配置类使用反射访问的实施。下面的代码片段显示了一个通用的静态方法来反序列化在文件系统或XML格式的资源配置节:
public static T DeserializeSection<T>(string config) where T : class

{

  T cfgSection = Activator.CreateInstance<T>();

  byte[] buffer = new ASCIIEncoding().GetBytes(config);

  XmlReaderSettings xmlReaderSettings = new XmlReaderSettings();

  xmlReaderSettings.ConformanceLevel = ConformanceLevel.Fragment;



  using (MemoryStream ms = new MemoryStream(buffer))

  {

    using (XmlReader reader = XmlReader.Create(ms, xmlReaderSettings))

    {

      Type cfgType = typeof(ConfigurationSection);

      

      MethodInfo mi = cfgType.GetMethod("DeserializeSection", 

          BindingFlags.Instance | BindingFlags.NonPublic);

          

      mi.Invoke(cfgSection, new object[] { reader });

    }

  }

  return cfgSection;

}

下面的代码片段显示了一个执行其名称所指定的服务,可以从位于所需的配置文件的ServiceModelSectionGroup反序列化的GetServiceElement:
public static ServiceElement GetServiceElement(

    string configurationName, string configFilename)

{

  ExeConfigurationFileMap filemap = new ExeConfigurationFileMap();

  

  filemap.ExeConfigFilename = string.IsNullOrEmpty(configFilename) ? 

    AppDomain.CurrentDomain.SetupInformation.ConfigurationFile : 

    Path.GetFullPath(configFilename);

      

  Configuration config = 

    ConfigurationManager.OpenMappedExeConfiguration(filemap,

        ConfigurationUserLevel.None);

      

  ServiceModelSectionGroup serviceModel = 

    ServiceModelSectionGroup.GetSectionGroup(config);



  foreach (ServiceElement se in serviceModel.Services.Services)

  {

    if (se.Name == configurationName)

    {

       return se;

    }

  }

  throw new ArgumentException(" ... ");

}
LocalRepositorySection
LocalRepositorySection代表为引导/装载机服务的自定义配置节。本节是位于应用程序配置文件,可以使用预先定义的元数据作为一个低水平库。下面的代码片段显示了其结构:
<LocalRepository enable="true" endpointName="boot">

  <HostMetadata hostName="Test" assemblyNames="WorkflowLibrary1; WorkflowLibrary2">

    <Services>

      <add name="Host.Test" appDomainHostName="test" />

      <add name="WorkflowLibrary1.Workflow6" appDomainHostName="*" />

      <add name="WorkflowLibrary1.Workflow4" appDomainHostName="*" 

        config="..\..\ESB\LocalRepository\Metadata\Workflow40.config" 

        xoml="..\..\ESB\LocalRepository\Metadata\WorkflowLibrary1.Workflow4.xoml"

        rules="..\..\ESB\LocalRepository\Metadata\WorkflowLibrary1.Workflow4.rules"/>

      <add name="WorkflowLibrary1.Workflow1" appDomainHostName="123" 

         config="Test_Workflow.exe.config" 

        wsdl="Workflow1.wsdl"

        xslt="Workflow1.xslt" />                   

    </Services>

  </HostMetadata>

</LocalRepository>

正如你可以看到,上面的部分声明一个目录中的每个服务的元数据。在LocalRepository部分有两个属性。这些声明的属性元数据远程下载,基于端点声明。例如:
<client>

  <endpoint name="boot" 

      address="net.pipe://localhost/LocalRepository" 

      binding="netNamedPipeBinding" 

      contract="RKiss.ESB.IBootstrap"/>

</client>

确定,现在我们已经全部由元数据驱动的的VirtualService模型支持。我们需要通过元数据服务/行动,以步行,并在其目录中的位置的顺序创建的所有要求VirtualServices。这项服务将只运行在启动过程(冷启动),这就是为什么我们称之为引导。
引导是通过调用一个quot;马methodquot启动,在启动过程中的权利的地方。下面的代码片段显示了这一行:
HostServices.Current.Boot(m_HostMetadata);

,m_HostMetadata是一个可选的参数传递到大会嵌入的元数据。请注意,此元数据的优先级最低,并可以在配置文件中或位于远程接收的元数据的元数据覆盖。
引导/装载机启动的服务,是一个VirtualServices非常强大的功能。它的实施有一个非常小的装配尺寸,但高负载在BOOTSTRAP.LOADER.AppDOMAINS任何复杂的服务解决方案的能力。结合在应用过程中的启动顺序的HostServices.Current.Boot,您的应用程序可以由元数据驱动的的逻辑模型的一部分​​。引导
引导是基于元数据资源的创建和初始化VirtualServices上拉的服务。 HostMetadata代表的物理模型,如应用程序的名称,域,端点,绑定的XOML,规则,XSLT等注意描述VirtualServices目录,引导并不需要知道什么逻辑模型和什么/谁创建元数据。引导所需的一个,也是唯一一个的信息是一个端点,以获取元数据。
下面的代码片段显示了一个引导的ServiceContract:
[ServiceContract]

public interface IBootstrap

{

  [OperationContract]

  HostMetadata GetMetadata(

    string hostName,     // logical name of the application

    string appName,     // application name (default appDomainName)

    string machineName,    // netbios machine name 

    string serviceName    // option: all services or specific service (IIS7/WAS)

    );       

}

储存库服务保存元数据的物理模型(或知道它在哪里),将发回的HostMetadata目录组织了一个元​​数据。基于此目录,引导(如果不存在)将创建一个AppDomain的主机和负载VirtualService有或没有的XOML工作流;见下面的图片:

引导拉服务的ESB基础设施的一部分,并且必须在默认的AppDomain创建。正如我前面提到的,逻辑的应用程序模型可以映射到业务的AppDomains驱动物理模型。上图为两个货仓,如存储所有创建和加载的AppDomain和存储业务的AppDomain有关VirtualServices(ServiceHostActivator)引用(HostServices)。注意,货仓线程安全的,并在位于数据插槽。HostServices
HostServices是在特定的AppDomain的托管VirtualServices的主要对象之一。它是一个线程安全控制器管理的AppDomains,如启动,负载,中止,开放的,等下图显示了一个类图的HostServices:

HostServices对象是发起在默认的AppDomain,其参考存储在域的数据插槽。第一次调用此对象将创建它的实例,见下面的代码片段:
public static HostServices Current

{

  get

  {

    string key = typeof(HostServices).FullName;

    HostServices hostservices = AppDomain.CurrentDomain.GetData(key) as HostServices;

    if (hostservices == null)

    {

      lock (AppDomain.CurrentDomain.FriendlyName)

      {

        hostservices = AppDomain.CurrentDomain.GetData(key) as HostServices;

        if (hostservices == null)

        {

          hostservices = new HostServices();

          AppDomain.CurrentDomain.SetData(key, hostservices);

        }

      }

    }

    return hostservices;

  }

}

HostServices也用于作为引导一部分引导DomainLoader服务启动服务。这项服务可以控制的HostServices远程存储,如装卸,在特定的AppDomain和重装VirtualServices。这是一个很大的特点VirtualService基础设施,其中一个逻辑模型可以控制物理部署,根据业务需要在一个孤立的方式。
因此,引导目录元数据应该包含少了一个服务,如DomainLoader,看到下面的配置片段例如:
<LocalRepository enable="true" endpointName="boot">

  <HostMetadata hostName="Application_Test" >

    <Services>

      <add name="RKiss.ESB.DomainLoader" />

    </Services>

  </HostMetadata>

</LocalRepository>

注意,默认的AppDomain是不便于管理,通过这项服务,因此,建议加载到默认的AppDomain。当然,这是可能加载一个自定义的AppDomain来管理其他的AppDomain,但内置在引导/装载机是最起码的的服务,使管理过程域。
下面的代码片段显示了一个HostServices方法实施样板的例子:
public void Add(string appDomainName, ServiceConfigData config, 

       Stream workflowDef, Stream rulesDef)

{

  try

  {

    _rwl.AcquireWriterLock(TimeSpan.FromSeconds(60));

    

    appDomainName = ValidateAppDomainName(appDomainName);

    AppDomain appDomain = this.CreateDomainHost(appDomainName);   

    ServiceHostActivator.Create(appDomain, config, workflowDef, rulesDef);

  }

  finally

  {

    _rwl.ReleaseWriterLock();

  }

}

正如你可以看到在上面的代码片断,有一个对象ServiceHostActivate VirtualService托管在特定的AppDomain(在此方法是一个XOML激活WorkflowService)来处理所有的魔力工作。让我们看看这种神奇的对象的内部。ServiceHostActivator
ServiceHostActivator是远程对象在AppDomain中处理托管服务。注意,Remoting是在同一进程中的域之间沟通的正确方法,因此,这个对象是从MarshalByRefObject类派生。一旦VirtualService已被加载到AppDomain中,其ServiceHostActivator存储在数据槽存储管理的目的,如打开,关闭,并中止。
下面的图片显示了一个ServiceHostActivator对象的类图:

在非默认AppDomain的托管服务的概念是基于目标的AppDomain启动一个远程ServiceHostActivator对象,然后调用其在目标域的业务处理方法。下面的代码片段演示了如何实现其创建方法是:
public static ServiceHostActivator Create(AppDomain appDomain, 

       ServiceConfigData config, Stream workflowDef, Stream rulesDef)

{

  string _assemblyName = Assembly.GetAssembly(typeof(ServiceHostActivator)).FullName;

  string typeName = typeof(ServiceHostActivator).ToString();

  

  // get proxy

  ServiceHostActivator activator = 

   appDomain.CreateInstanceAndUnwrap(_assemblyName, typeName) as ServiceHostActivator;

  

  // remoting call

  activator.SetHost(config, workflowDef, rulesDef);

  

  return activator;

}

下面的代码片段显示了主办一个VirtualService远程处理方法的实施:
private void SetHost(ServiceConfigData config,

        Stream workflowDef, Stream rulesDef, string baseAddresses)

{

  try

  {

    if (_host == null)

    {

      // workaround for passing file/string to the override ApplyConfiguration

      CallContext.SetData("_config", config.Config);



      _host = new WorkflowServiceHostESB(config, workflowDef, rulesDef, 

          BaseAddresses(baseAddresses));

          

      _host.Faulted += new EventHandler(_host_Faulted);

      

      this.AddToStorage(this);

    }

    else

    {

      throw new InvalidOperationException("The ServiceHost already exists");

    }

  }

  finally

  {

    CallContext.FreeNamedDataSlot("_config");

  }

}

这是所有申办(引导)基于元数据的主机进程的VirtualServices。让我们继续前进的另一个重要组成部分的VirtualService设计和概念,消息中介。可介导的WCF消息(通过扩展名)和/或工作流模型。我将重点放在调解的XOML激活WorkflowService消息,这给了我们更多的逻辑模型中的虚拟化和可重用性。中间人
调解员是一个可插拔的组件在消息流中集成服务,以透明的方式处理与他们的消费者。调解员是一到一个消息处理器。

protected override ActivityExecutionStatus Execute(ActivityExecutionContext context)

{

  if (executionContext == null)

    throw new ArgumentNullException("context");



  // bypass status

  ActivityExecutionStatus status = ActivityExecutionStatus.Closed;



  // pre-processing

  this.OnInvoking(EventArgs.Empty);

  base.RaiseEvent(Mediator.InvokingEvent, this, EventArgs.Empty);



  // action

  if (MediatorMode == MediatorMode.Custom)

  {

    status = base.Execute(context);

  }

  else if (MediatorMode != MediatorMode.None && MessageInput != null)

  {

    if (Mediators == null && OperationContext.Current != null)

    {

      ServiceConfigDataService service =

       OperationContext.Current.Host.Extensions.Find<ServiceConfigDataService>();

      

      if (service != null)

      {

        string xslt = service.ServiceConfigData.Xslt;

        Mediators = ServiceMediatorsSection.GetMediators(xslt, 

            MessageInput.Headers.Action, MediatorMode);

      }

    }

    if (Mediators != null)

    {

      status = base.Execute(context);

    }

    else 

    {

      using(MessageBuffer buffer = MessageInput.CreateBufferedCopy(int.MaxValue))

      {

        MessageOutput = buffer.CreateMessage();

      }

    }

  }   

  else

  {

    using(MessageBuffer buffer = MessageInput.CreateBufferedCopy(int.MaxValue))

    {

       MessageOutput = buffer.CreateMessage();

    }  

  }  

     

  // post-processing

  this.OnInvoked(EventArgs.Empty);

  base.RaiseEvent(Mediator.InvokedEvent, this, EventArgs.Empty);

  return status;

}

<operation action='http://tempuri.org/ITest3/Ping3'> 

 <output> 

  <mediator name='abcd' xpath='/' validation='false'>

   <xslt>

    <xsl:stylesheet version='1.0' 

     xmlns:xsl='http://www.w3.org/1999/XSL/Transform'

     xmlns:a='http://www.w3.org/2005/08/addressing'

     xmlns:s='http://www.w3.org/2003/05/soap-envelope'>

     <xsl:output method='xml' omit-xml-declaration='yes' />

     <xsl:param name='result'>?</xsl:param>     

     <xsl:template match='/'>

       <s:Envelope>

        <s:Header>

          <a:Action s:mustUnderstand='1'>

            http://tempuri.org/ITest3/Ping3Response

          </a:Action>

        </s:Header>

        <s:Body>

          <Ping3Response xmlns='http://tempuri.org/'>

            <Ping3Result>

              <xsl:value-of select='$result'/>

            </Ping3Result>

          </Ping3Response>

         </s:Body>

       </s:Envelope>

     </xsl:template>    

    </xsl:stylesheet>

   </xslt>

   <params>

     <param name='result' namespace='' value='Hello World'/>

   </params> 

   <funcions />

  </mediator> 

 </output>

</operation>
<ns2:Mediator x:Name="Mediator1" 

         MediatorMode="Output" 

         MessageInput="{ActivityBind Workflow6,Path=_message}" 

         MessageOutput="{ActivityBind Workflow6,Path=_returnValue}">

  <ns2:XslProcessor x:Name="XslProcessor1" 

         Mediators="{ActivityBind /Parent,Path=Mediators}"

         MediatorName="om3" 

         MessageInput="{ActivityBind /Parent,Path=MessageInput}" 

         MessageOutput="{ActivityBind xslProcessor2,Path=MessageInput}">

       <ns2:XslProcessor.Parameters>

         <WorkflowParameterBinding ParameterName="result">

           <WorkflowParameterBinding.Value>

             <ActivityBind Name="/Parent" Path="MessageInput.Headers.Action"/>

           </WorkflowParameterBinding.Value>

         </WorkflowParameterBinding>

       </ns2:XslProcessor.Parameters>

  </ns2:XslProcessor>

</ns2:Mediator>

[ServiceContract(Namespace = "urn:rkiss.esb", 

 SessionMode = SessionMode.NotAllowed)]

public interface IBroadcast

{

    [OperationContract(IsOneWay = true, Action = "*")]           

    void ProcessMessage(Message message);

}

class FilteringEndpointBehavior : IEndpointBehavior

{

  MessageFilter _filter = null;

  string _xpath = string.Empty;

  bool _enable = true;



  public FilteringEndpointBehavior(string xpath, bool enable)

  {

    this._xpath = xpath;

    this._enable = enable;

  }

  

  public FilteringEndpointBehavior(MessageFilter filter, bool enable)

  {

    this._filter = filter;

    this._enable = enable;

  }

 

  public void ApplyClientBehavior(ServiceEndpoint endpoint, 

     ClientRuntime clientRuntime)

  {

    throw new InvalidOperationException("This behavior is not supported");

  }

  

  public void ApplyDispatchBehavior(ServiceEndpoint endpoint, 

       EndpointDispatcher endpointDispatcher)

  {

   if (this._enable)

   {

     endpointDispatcher.AddressFilter = this._filter != null ? 

      this._filter : 

      new XPathMessageFilter(this._xpath, new XPathMessageContext());

   }

  }

  

  public void AddBindingParameters(ServiceEndpoint endpoint, 

         BindingParameterCollection bindingParameters){}

  public void Validate(ServiceEndpoint endpoint){}



}
public class FilteringEndpointBehaviorExtension : BehaviorExtensionElement

{

  public const string ExtensionName = "filter";



  protected override object CreateBehavior()

  {

    return new FilteringEndpointBehavior(XPath, Enable);

  }



  public override Type BehaviorType

  {

    get { return typeof(FilteringEndpointBehavior); }

  }



  [ConfigurationProperty("xpath", DefaultValue = "", IsRequired = true)]

  [StringValidator(MaxLength = 1024)]

  public string XPath

  {

    get { return (string)base["xpath"]; }



    set { base["xpath"] = value; }

  }



  [ConfigurationProperty("enable", DefaultValue = true, IsRequired = false)]

  public bool Enable

  {

    get { return (bool)base["enable"]; }

    set { base["enable"] = value; }

  }

}
<endpoint 

  address='net.p2p://broadcastMesh/servicemodelsamples/announcements'

  binding='netPeerTcpBinding' 

  bindingConfiguration='xpathAddressFilter' 

  contract='RKiss.ESB.IBroadcast'>

</endpoint>

protected override void OnStart(string[] args)

{

    HostServices.Current.Boot();

}



protected override void OnStop()

{

    HostServices.Current.Close();

}
<LocalRepository enable="true" endpointName="boot">

  <HostMetadata hostName="Application_Test">

    <Services/>

  </HostMetadata>

</LocalRepository>



<system.serviceModel>

  <client>

    <endpoint name="boot" 

      address="net.pipe://localhost/LocalRepository" 

      binding="netNamedPipeBinding" 

      bindingConfiguration="bootstrap" 

      contract="RKiss.ESB.IBootstrap" />

  </client>

  <bindings>



    <netNamedPipeBinding>

      <binding name="bootstrap" >

        <readerQuotas maxStringContentLength="1000000" /> 

        <security mode="None"/>

      </binding>

    </netNamedPipeBinding>

  </bindings>

</system.serviceModel>
public class VirtualServiceFactory : ServiceHostFactoryBase

{

  public override ServiceHostBase CreateServiceHost(string constructorString,

       Uri[] baseAddresses)

  {

    ServiceHostBase service = null;



    if (string.IsNullOrEmpty(constructorString))

         throw new ArgumentException(constructorString);



    string serviceName = constructorString.Trim(new char[] { ' ', '"' });



    using (HostServices services = new HostServices())

    {

      HostMetadata catalog = services.CreateCatalog(serviceName, null);



      if (catalog.Count == 0)

        throw new ArgumentOutOfRangeException("Missing metadata");



      // only one service is supported

      if (catalog.Count != 1)

         throw new ArgumentOutOfRangeException("Only one service is required");



      // only service for current appDomain is supported

      string appDomainHostName = catalog[0].AppDomainHostName;

      

      if (string.IsNullOrEmpty(appDomainHostName) || 

          appDomainHostName == "*" || appDomainHostName.ToLower() == "default")

      {

        // create service

        services.CreateServices(catalog);

        

        // hosted service

        service = services.GetHostedService(serviceName, true);

     }

      else

      {

        throw new ArgumentOutOfRangeException("AppdDomainHostName");

      }

    }

    return service;

  }

}
<%@ServiceHost 

    Language="C#" 

    Debug="true" 

    Service="WorkflowLibrary1.Workflow6" 

    Factory="RKiss.ESB.VirtualServiceFactory"%>

Visual Studio 2008中






参考文献:




&# 160;


 60;



  ;





...
& #160;



 60;


 0;









<xsl:value-of select='x:FirstName'/>

<xsl:text>/</xsl:text>

<xsl:value-of select='ext:Name(x:FirstName, x:LastName)'/>

<xsl:text>/</xsl:text>

<xsl:value-of select='x:LastName'/>

<xsl:value-of select="concat(x:FirstName,'/function=',x:FirstName,'_',x:LastName,'/',x:LastName)"/>





感谢



谢谢。

至于


感谢



感谢


谢谢。

丹尼尔沃恩

回答