面向消息

SQL Server 2005 Service Broker:Microsoft 已经新邮件技术

Juan Carlos (John Charles) Olamendy Turruellas

SQL Server 2005,Microsoft 引入了该服务代理 (SSB) 为技术支持代理设计模式和面向消息的中间件 (MOM) 的原则。这一技术但是,已经一些用于,尽管一个传统的同步请求/响应的方法以相反的 SSB,能力使开发人员通过来构建可靠、 可伸缩、 分布式,和异步消息传递应用程序实现的 SQL Server 关系数据库功能与组合的消息队列机制。

一个代理是一个软件组件,它位于之间需要集成的企业应用程序和与其进行交互。当应用程序需要互相交换数据连接时,此中间件隐藏通信协议和拓扑结构的复杂性。它还可以提供功能 (如转换和消息路由,管理这种方式业务流程逻辑的执行。

在这种模型作为一种服务公开的功能,然后服务之间的数据的交换通过无论在同一时间是否联机应用程序所涉及的邮件。此模型还使松散耦合的复合应用程序,并保证消息不会丢失。在本文中,我将介绍的图解显示了基于消息的复合解决方案的可能性的实际业务方案的 SSB 的主要概念。

应用程序的 SSB 实际业务方案

您可以在其中应用 SSB 技术主上下文是企业级应用程序的集成解决方案的开发。这些应用程序通常支持管理和它们之间的信息的交换需要可靠的面向消息中间件 (没有消息将丢失) 的业务流程的自动化。鍙 ﹀ 的方式  这些应用程序驻留在一个复杂的环境具有大量的系统和用户。不是所有应用程序联机一次,其处理的松散耦合的方式的请求可能需要一个长的时间。

在后端系统之间的通信是同步的一个传统的请求-响应模型中和业务流程逻辑实现 (使用高级语言如 C# 或 Java) 通过您的解决方案和一系列数据库事务的业务层中的组件。此的方法如果一个后端系统,关闭整个进程将传递到待机状态。渚嬪的方式  您不能授权信用卡如果付款系统关闭。

SSB 应用程序的主要目标是集成企业系统通过一个常见的通讯通道。某些外部应用程序 (ERP、 供应链管理、 CRM,和其他) 或内部模块 (T-SQL 代码和 CLR 管理触发器和存储过程) 将邮件放入队列,与其对应的接收邮件的通知时则位于队列 (和需要进行检索和出列) 通过触发器或常规 SEND/接收操作。此处构想基于企业系统的服务器端组件可以立即接受请求,但推迟返回到客户端,该值指示它应该等待响应更高版本的信息性消息的所请求的处理。

示例异步 SSB 解决方案这篇文章中所示 (请参阅的图 1) 包括门户网站和存储区和库存的数据库。该门户的网站具有两个网页:一个用于输入与一个订单的请求和另一个用于查看订单的状态相关联的数据。存储数据库应用程序从门户网站接收顺序请求,此数据保持到在表 tbOrder 并再过帐库存数据库应用程序侦听的通信通道中的一个常见总线在队列中的邮件的窗体中,顺序请求数据。清单数据库接收传入的请求订单,并处理仓库库存和通讯组传递。它还创建一个基本的顺序响应并发送,通过到存储数据库的另一个队列。最后,存储数据库侦听顺序响应消息,并更新基础顺序请求中的行 (确定或错误的状态) 表 tbOrder,允许用户检查订单的状态的状态。这种方式,分布式的解决方案委托 SSB 基础结构 (应用代理设计模式) 的所有消息传递、 备份、 管理和故障转移过程,和集中其精力问题域。


图 1 复合解决方案的企业系统作为的电子商务方案

从 SSB 原则的视点,从存储数据库应用程序开始对话库存数据库应用程序提交到库存应用程序通过公共接口,基于服务的队列的顺序请求消息。服务是作为邮件存储库中使用队列并指定其功能使用合同的 SSB 技术的逻辑终结点。合同指定一个给定的对话中的邮件和它们的基础消息类型的方向。邮件放入队列 (排队) 后,存储应用程序将继续执行其他任务。库存应用程序时 (这可能是在晚上或其他的空闲时间) 的准备、 一个服务程序接受消息从队列和它,和库存数据库应用程序发送消息的确认订单的过程已被处理,最终结果一起。(请参见图 2)。


图 2的 SSB 解决方案体系结构

为您分析 SSB 解决方案体系结构,您可能会识别的基础的集成解决方案可以实现使用等,MSMQ 和 Microsoft BizTalk Server 其他消息技术。但是,along with 在 Service Broker Microsoft SQL Server 提供要构建复合应用程序在其上一个可靠的基础结构,可恢复性、 可维护性、 持久性、 性能和可伸缩性,可以只在数据库系统中找到的级别。在 SSB 服务质量功能包括:

  • 交易记录。如果传出队列的执行一条消息,并且出现错误该消息不会丢失。
  • 可恢复性。如果由于媒体故障系统失败,处理器中或主内存中,或系统问题所经历的中断,邮件必须 durably 保持继续处理是还原系统。
  • 可扩展性。一旦邮件投递到队列,它们的处理不引入性能问题,添加更多的资源时的速率。工作负荷可能增加到数千个每秒,封邮件,但可以通过在多个参与者 SSB 实例之间分布工作平衡工作负荷。

实现解决方案

在本节中,我 ’ll 介绍如何实现 SSB 业务方案的解决方案。主体的数据库是存储和库存数据库,不同的服务器上承载的。这意味着您需要建立使用 SQL Server Management Studio 这些系统的连接。

第一步是通过在系统目录 (主数据库) 中配置基础实例级别启用两个数据库 (请参见图 3 的) 中的 Service Broker 以及传输安全性功能。

图 3 启用 SSB 和传输安全性功能,在参与数据库

--- Configure Instance1.
use master;
alter database Store set ENABLE_BROKER;
alter database Store set TRUSTWORTHY ON;
use Store;
create master key encryption by password = 'Store*-';

--- Configure Instance2.
use master;
alter database Inventory set ENABLE_BROKER;
alter database Inventory set TRUSTWORTHY ON;
use Inventory;
create master key encryption by password = 'Inventory*-';

安全设置

下一步是配置两个 SQL Server 实例和数据库之间的通信基础结构,以建立一个涉及身份验证和 (可选),加密的邮件的安全通道。 您可以使用 Windows 或基于证书的身份验证。

在 Windows 机制在每个 SQL Server 实例登录到其他使用进程正在运行的 Windows 用户帐户的凭据。 ’s 需要它的两个 SQL Server 实例都使用域用户帐户具有权限作为服务登录并发送邮件。

该证书机制在每个 SQL Server 实例在其他通过验证使用数字证书。 SQL Server 2005,’s 需要它您可以设置一个公钥基础结构,在第一步生成公钥/私钥对。 私钥受主数据库的数据库主密钥,并主要用于建立系统已经标识交换数据使用私钥加密和验证使用在另一面的公钥。 然后每个 SSB 终结点配置为使用该生成的证书进行身份验证和加密。

消息结构和验证

下一步,您需要指定 XML 架构、 以及消息类型、 队列、 合同,用于此安全通道上的服务在基础的通讯对象。 一条消息在传输的数据的内容持有者,每个实例都有相关联的消息的类型。 若要确定 interchanged 消息的结构,使用消息类型指定结构和基础消息实例的约束。 这可以是一个简单的二进制对象、 有效的 XML 文档也可以是一个格式良好的 XML 文档。 我可以使用 XML 标准来表示和我们的集成解决方案中,邮件的格式。 在此业务方案第一条消息表示顺序从存储数据库发送到库存数据库使用格式正确的 XML 文档 (由 OrderRequestMsgType 消息类型和 OrderRequestSchema 架构描述和包含有关顺序标识符、 客户标识符、 订单日期,和请求的项的标识符、 价格,和数量的数据)。 第二条消息表示相关的响应发送的目标数据库太使用一个格式正确的 XML 文档 (由 OrderResponseMsgType 消息类型和 OrderResponseSchema 架构和包含的数据描述相关的邮件请求有关,处理状态和说明)。 图 4 定义 XML 架构用于指定和验证订单请求和订单响应消息的结构。

图 4 订单要求订单响应的 XML 架构的消息

--- Create the schemas.
create xml schema collection OrderRequestSchema as
N'
<xs:schema xmlns="http://www.mycorporation.com/2007_schemas/" 
xmlns:xs="http://www.w3.org/2001/XMLSchema" 
targetNamespace="http://www.mycorporation.com/2007_schemas/" 
elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:element name="Order">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="LineItem">
                    <xs:complexType>
                        <xs:attribute name="ItemNumber"
                                      type="xs:int"/>
                        <xs:attribute name="Quantity"
                                      type="xs:int"/>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
            <xs:attribute name="Id" type="xs:int"/>
            <xs:attribute name="Customer" type="xs:int"/>
            <xs:attribute name="Order_date" type="xs:dateTime"/>
        </xs:complexType>
    </xs:element>
</xs:schema>
'
go


create xml schema collection OrderResponseSchema as
N'
<xs:schema xmlns="http://www.mycorporation.com/2007_schemas/" 
xmlns:xs="http://www.w3.org/2001/XMLSchema"
 targetNamespace="http://www.mycorporation.com/2007_schemas/" 
elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:element name="OrderConfirmation">
        <xs:complexType>
            <xs:sequence>
               <xs:element name="Id" type="xs:int"/>
                     <xs:element name="Processing_Status" type="xs:int"/>
                     <xs:element name="Processing_Description"
                       type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>
'
Go

以下代码创建两个基于以前的架构是 SSB 服务的接口的一部分的消息类型的系统上。

create message type OrderRequestMsgType validation= VALID_XML 
             with schema collection OrderRequestSchema;
 

create message type OrderResponseMsgType validation=VALID_XML 
             with schema collection OrderResponseSchema;
go

该架构集合的子句表示该 SSB 验证已定义的 XML 架构对邮件。 SSB 执行验证,只要目标服务接收邮件。 如果内容 doesn’t 通过验证,SSB 返回错误消息。 它 ’s 值得指出消息验证可以相当昂贵。 因此,’s 建议启用用于处理来自不受信任的源,因为早期 ; 可以被发现坏消息的消息验证否则,此功能可以被关闭。 您可以引用 SQL Server 联机丛书更好地了解消息类型创建语法。

通信基础结构的定义

创建消息类型之后, 您需要定义合同服务可以发送或接收指定哪些邮件这两个系统上 (和哪个方向)。 合同还确定是否将消息发送由会话的发起方、 对话的目标或由发起方或会话的目标。 在本例中,发起者是在存储数据库,并且目标是清单数据库。 发送 BY 子句指定终结点所涉及的基础的角色。 SSB 确保仅在合同中定义的消息类型的处理和处理。 如果服务发送另一种消息类型,被拒绝消息,然后閿欒  娑堟伅发回。

create contract OrderProcessingContract
       (OrderRequestMsgType sent by initiator,OrderResponseMsgType sent by
         target)
go

还需要创建一个名为 tbOrder 记录顺序请求一起处理状态中的数据在存储数据库上表和 (返回从库存数据库) 的说明,以便客户端可以通过使用 Web 页检查顺序。 下面的代码演示如何创建表。 表中的该 OrderStatus 字段可以具有三个值之一:

  • 值为 0。 不处理 ; 顺序OrderStatusDescription 字段是空的。
  • 值为 1。 没有错误 ; 处理顺序OrderStatusDescription 字段是空的。
  • 值为 2。 有错误 ; 处理顺序在 OrderStatusDescription 字段中的详细信息。
create table tbOrder
(
   nOrderID int identity(1,1) primary key,
   nCustomer int,
   dtOrderDate datetime,
   nItemNumber int,
   nItemQuantity int,
   nOrderStatus int,
   vcOrderStatusDescription varchar(50)
);

若要将输入在表 tbOrder 在 Web 门户前端的订单数据您可以定义存储的过程所示的图 5, 主界面,以提交基础数据,并返回一个顺序编号。 订单号码是按时间显示在其他某些时候,并由用户用来请求基础订单状态。

图 5 创建存储过程 spInsertOrder

create procedure spInsertOrder
  @nOrderID int output,
  @nCustomer int,
  @dtOrderDate datetime,
  @nItemNumber int,
  @nItemQuantity int
as
begin
  insert into tbOrder(nCustomer, dtOrderDate, nItemNumber, nItemQuantity,
    nOrderStatus)
  values(@nCustomer, @dtOrderDate, @nItemNumber, @nItemQuantity, 0);
  set @nOrderID=@@IDENTITY;
end;

下一步是创建用于存储所收到的邮件 (或者从目标服务或发起方服务器) 必须处理的队列。 当 SSB 接收并验证一条消息 (如前面所述) 时,它邮件队列中插入。 (您可以引用 SQL Server 联机丛书的语句创建的队列的多个文档)。

队列可以绑定到存储过程 (只要其中一个发送到队列) 将自动处理邮件,并处理它们。 创建中的一个重要参数是队列的 MAX_QUEUE_READERS,指定的队列开始在同一时间的激活存储过程的实例的最大数量。 MAX_QUEUE_READERS 的值必须是介于 0 和 32,767。 如果希望顺序请求 ’re 比一个读取器可以处理更快地速率的速率,您可以指定多个队列读取器实例化多个读取器根据直到达到最大数量。 它 ’s 值得注意在队列中之前,必须创建此存储的过程,但为了的这篇文章,此存储过程的业务逻辑部分所述 “ SSB 技术中的服务之间的对话。 ” 在示例中,我们 ’re 读取队列和自动,处理该消息,但某些商业方案要求在用户,干预,这意味着必须通过使用公开基础逻辑作为存储过程从图形用户界面调用读取队列。

此时,我们创建了两个系统的建立双向通信通道和其常见了解数据库对象。 下一步,您需要创建每个系统提供订单处理功能的项目。

首先您需要存储数据库 (基础的存储的过程 spStoreDB_QueueReader 必须创建第一次 ; 上创建队列请参阅图 8) 保存和处理传入响应顺序邮件和更新订单状态中,表 tbOrder。

--- Create the queue on the Store database
create queue StoreQueue with status=on,
  activation (procedure_name=spStoreDB_QueueReader,max_queue_readers=5,
  execute as 'dbo');
go

您在下一步 (基础的存储的过程 spStoreDB_QueueReader 必须创建第一次 ; 在库存数据库上创建另一个队列请参见图 7) 接收顺序从存储区的请求消息数据库并处理它们。

--- In the Inventory database
create queue InventoryQueue with status=on,
  activation (procedure_name=spInventoryDB_QueueReader,max_queue_readers=5,
  execute as 'dbo');
go

现在您需要定义以下代码所示的该服务。 服务代表 SSB 解决方案中的应用程序级终结点,并且可以在发起方和消息的目标。 服务可以绑定到多个队列和合同的组合。 SSB 可以在一个新邮件到达队列,并且 SSB 或者可以安排激活的事件或手动执行该服务时自动激活服务。 在这两个数据库系统中独立创建 SSB 服务。

--- In the Store database
create service StoreService on
       queue StoreQueue(OrderProcessingContract);
go

--- In the Inventory database
create service InventoryService on
       queue InventoryQueue(OrderProcessingContract);
go

而且您还需要创建一个要传递来自服务的远程终结点的消息的路由。 路由是一起绑定服务的通信信道。

--- Create route to InventoryService from Store database
create route Route2Inventory
     with service_name = 'InventoryService',
          address = 'tcp://Instance2:4037';

--- Create route to StoreService from Inventory database
create route Route2Store
     with service_name = 'StoreService',
          address = 'tcp://Instance1:4037';

到目前为止您 ’re 准备好创建映射用来与远程 SSB 终结点打开会话的凭据的远程服务绑定。 此处 ’s 绑定的代码。

--- Create remote service binding on Store database
create remote service binding ToInventory_RSB
     to service 'InventoryService'
     with user = InventoryUser;


--- Create remote service binding on Inventory database
create remote service binding ToStore_RSB
     to service 'StoreService'
     with user = StoreUser;

SSB 技术中的服务之间进行对话

要建立 SSB 中的服务之间的通信通道,您需要做的只是开始对话。 通过使用在 BEGIN 对话框对话语句来创建此对话,并每个实例都有唯一的句柄表示的数据交换通道。 您可以通过定义消息类型和其内容传递邮件通过特定的打开会话使用 SEND 语句。 最后,要对话结束对话语句使用。

图 6 显示由用户将输入订单的请求数据、 记录此信息在表 tbOrder、 存储数据库和清单数据库通过合同和以前,创建服务之间事务对话,然后通过此对话中发送的 XML 架构 OrderRequestSchema 顺序请求文档实例的调用存储的过程。 由用户通过 Web 门户中的订单请求数据输入页中调用此存储的过程。

图 6 存储数据库上的存储的过程 EnterOrderRequest

--- Create the stored procedure EnterOrderRequest on the Store database
create procedure spEnterOrderRequest
  @nOrderID int output,
  @nCustomer int,
  @nItemNumber int,
  @nItemQuantity int
as
begin
  declare @dialog_handle uniqueidentifier;
  declare @Order_XDoc xml(OrderRequestSchema);
  declare @dtNow datetime;
  
  set @dtNow = getdate();
  begin transaction
     exec spInsertOrder @nOrderID 
output,@nCustomer,@dtNow,@nItemNumber,@nItemQuantity;

     set @Order_XDoc =
       '<Order xmlns="http://www.mycorporation.com/2007_schemas/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://www.mycorporation.com/2007_schemas/" 
          Id="'+cast(@nOrderID as varchar(10))+
              '" Customer="'+cast(@nCustomer as varchar(10))+'" 
              Order_date="'+convert(varchar,@dtNow,126)+'-05:00">
          <LineItem ItemNumber="'+cast(@nItemNumber as varchar(10))+
              '" Quantity="'+cast(@nItemQuantity as varchar(10))+'"/>
       </Order>';


     begin dialog conversation @dialog_handle
     from service StoreService
     to service 'InventoryService'
     on contract OrderProcessingContract;


     send on conversation @dialog_handle 
     message type OrderRequestMsgType(@Order_XDoc);
  commit;
end;

您可以通过在系统表 sys.transmission_queue 上执行诸如以下查询并查看该字段 transmission_status 中发现閿欒  娑堟伅结果集检查传输的状态。

---- Check the transmission status on the Store database.
select *
from sys.transmission_queue;

现在您还可以查看在邮件到达收件人队列 (在此例 InventoryQueue 队列中) 使用的 SELECT 语句通过基础的队列,并分析结果集的。

select message_type_name, cast(message_body as xml) message, queuing_order,
       conversation_handle, conversation_group_id
from InventoryQueue;

下一步中,您需要处理传入请求消息顺序,并发送到存储数据库状态为实际处理的订单响应。 首先,我们 ’ll 创建存储的过程实现队列读取器与 InventoryQueue 队列相关联的逻辑。 若要启动,您需要从使用该语句接收 TOP (1) 库存队列接收订单请求消息。 您检索顺序和项目标识符使用 XML 数据类型的值 XQuery 方法,并检查测试数据库 AdventureWorks Production.Product 在表中是否存在产品。 下一步创建顺序响应消息与适当的处理状态和说明、 发送此邮件通过使用语句发送对话,打开的会话对话框并最后结束使用该语句结束对话对话。 代码如图 7 所示。

接收语句类似于 SQL SELECT 语句中是中,您可以查询数据库对象 (在本例中一个队列) 并结果赋值给变量。 接收语句删除消息队列接收这些,与不同的 SELECT 语句不会从表中删除记录后。 XQuery 是新兴的查询语言,XML 数据源。 它使用地址的特定部分的文档的 XPath 表达式。 渚嬪的方式  表达式 (/ ns:Order/@Id)[1] 允许您选择传输 XML 文档中的第一个排序元素的属性 ID 子。 作为一种查询语言,主要构思 XQuery,但现在它 ’s 增强提供如 XSLT 转换功能。

图 7 InventoryQueue 队列从到读取邮件中创建该存储过程 spInventoryDB_QueueReader

--- Create stored procedure to process the incoming order request message
create procedure spInventoryDB_QueueReader
as
begin
  declare @Conv_Dialog_Handle uniqueidentifier;
  declare @Conv_Group_Handle uniqueidentifier;
  declare @Order_XDoc xml(OrderRequestSchema);
  declare @OrderConfirmation_Text varchar(8000);
  declare @OrderConfirmation_XDoc xml(OrderResponseSchema);
  declare @nOrderId int;
  declare @nItemNumber int;
  declare @nItemQuantity int;
  declare @nItemNumberResult int;
  declare @nProcessing_Status int;
  declare @vcProcessing_Description varchar(120);


  begin transaction;
    --- Receive the message from the queue
    receive top(1) @Order_XDoc = message_body,
                   @Conv_Dialog_Handle = conversation_handle,
                   @Conv_Group_Handle = conversation_group_id
    from InventoryQueue;


    --- Retrieve the order identifier
    select @nOrderId = @Order_XDoc.value
('declare namespace ns="http://www.mycorporation.com/2007_schemas/";(/ns:Order/@Id)[1]', 'int');
    select @nItemNumber = @Order_XDoc.value
('declare namespace ns="http://www.mycorporation.com/2007_schemas/";
(/ns:Order/ns:LineItem/@ItemNumber)[1]', 'int');
   

    --- Process the incoming order request
    select @nItemNumberResult=ProductID
    from AdventureWorks.Production.Product
    where ProductID=@nItemNumber;


    if (@nItemNumberResult is null)
    begin
       set @nProcessing_Status = 2;
       set @vcProcessing_Description = 'Processing transaction not
                                       'successfully. No product in the
                                       'Inventory database';
    end
    else
    begin
       set @nProcessing_Status = 1;
       set @vcProcessing_Description = 'Processing transaction successfully.
                                       'Found product in the Inventory 
                                       'database';
    end;


    --- Create the Order Confirmation message as a response
    set @OrderConfirmation_Text = 
            '<OrderConfirmation xmlns="http://www.mycorporation.com/2007_schemas/"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://www.mycorporation.com/2007_schemas/">
                <Id>'+cast(@nOrderId as varchar(10))+'</Id>
                <Processing_Status>'+cast(@nProcessing_Status as varchar(10))+'</Processing_Status>
                <Processing_Description>'+@vcProcessing_Description+'</Processing_Description>
            </OrderConfirmation>';


    set @OrderConfirmation_XDoc = @OrderConfirmation_Text;


    ---- Send the message through the open conversation dialog (@Dialog_Handler)
    send on conversation @Conv_Dialog_Handle
    message type OrderResponseMsgType(@OrderConfirmation_XDoc);


    ---- End the conversation
    end conversation @Conv_Dialog_Handle;
  commit;
end;
go

在存储数据库中,可以检查确认浣跨敤浠 ヤ 笅浠 g 爜顺序请求的响应。

---- Check the messages on the StoreQueue on the Store database.
select message_type_name, cast(message_body as xml) message, queuing_order,
       conversation_handle, conversation_group_id
from StoreQueue;

现在我们 ’ll 创建存储的过程 spStoreDB_QueueReader,实现逻辑的队列读取器与 StoreQueue 队列相关联的。 它使用相同的逻辑 spInventoryDB_QueueReader 为从该 StoreQueue 中获取邮件并更新该表格 tbOrder 处理状态和说明。 图 8 显示了该代码。

图 8 创建该存储过程 spStoreDB_QueueReader 到读取邮件 StoreQueue 队列中。

--- Create stored procedure to process the order response message
create procedure spStoreDB_QueueReader
as
begin
  declare @Conv_Dialog_Handle uniqueidentifier;
  declare @Conv_Group_Handle uniqueidentifier;
  declare @Order_XDoc xml(OrderResponseSchema);
  declare @nOrderId int;
  declare @nProcessing_Status int;
  declare @vcProcessing_Description varchar(50);


  begin transaction;
    --- Receive the message from the queue
    receive top(1) @Order_XDoc = message_body,
                   @Conv_Dialog_Handle = conversation_handle,
                   @Conv_Group_Handle = conversation_group_id
    from StoreQueue;


    --- Retrieve the order identifier
    select @nOrderId = @Order_XDoc.value('declare namespace 
ns="http://www.mycorporation.com/2007_schemas/";(/ns:OrderConfirmation/ns:Id)[1]', 'int');
    select @nProcessing_Status = @Order_XDoc.value('declare namespace 
ns="http://www.mycorporation.com/2007_schemas/";(/ns:OrderConfirmation/ns:Processing_Status)[1]', 'int');
    select @vcProcessing_Description = @Order_XDoc.value('declare namespace 
ns="http://www.mycorporation.com/2007_schemas/";
(/ns:OrderConfirmation/ns:Processing_Description)[1]', 'varchar(50)');
   

    update tbOrder
    set nOrderStatus=@nProcessing_Status,
      vcOrderStatusDescription=@vcProcessing_Description
    where nOrderID=@nOrderId;
  commit;
end;
go

最后,您需要实现我们的解决方案的前端在门户网站的组件。 在门户网站具有两个页面,请求的顺序输入项页和订单状态页。 顺序请求输入页允许用户输入订单请求的数据,并调用存储的过程 spEnterOrderRequest,实现主要业务逻辑与通过使用前面所述,Service Broker 管道处理订单相关联的。 以后检查订单状态显示顺序标识符。 订单状态页使用户能够通过输入订单标识符检查订单状态。

我使用 Visual Studio 创建一个新的 Web 站点,添加两个 Web 窗体。 第一个 OrderRequest_EntryPage.aspx,包含三个标签来描述三个文本框 Web 控件 m_tbItemQuantity、 m_tbItemNumber m_tbCustomerID) 获取有关在顺序请求和按钮的数据的 Web 控件 (m_btSubmit),它使我们的解决方案的业务逻辑的执行和顺序标识符输出标签 (m_lbOutput) 中的显示中输入字段。 图 9 显示页的代码。 该代码与 OrderRequest_EntryPage.aspx 图 10 中所示。

图 9 的 OrderRequest_EntryPage.aspx 的布局。

<%@ Page Language="C#" AutoEventWireup="true" 
CodeFile="OrderRequest_EntryPage.aspx.cs" Inherits="OrderRequest_EntryPage" %>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Order Request Entry page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <table border="0" width="100%" cellpadding="0" cellspacing="0">
          <tr>
            <td style="width:25%">
                <asp:Label ID="Label1" runat="server" Text="Enter
                  CustomerID"></asp:Label>            
            </td>
            <td style="width:75%">
                <asp:TextBox ID="m_tbCustomerID"
                  runat="server"></asp:TextBox>
            </td>
          </tr>  
          <tr>
            <td style="width:25%">
                <asp:Label ID="Label2" runat="server" Text="Enter Item
                  Number"></asp:Label>            
            </td>
            <td style="width:75%">
                <asp:TextBox ID="m_tbItemNumber" 
                  runat="server"></asp:TextBox>
            </td>
          </tr>
          <tr>
            <td style="width:25%">
                <asp:Label ID="Label3" runat="server" Text="Enter Item
                  Quantity"></asp:Label>
            </td>
            <td style="width:75%">
                <asp:TextBox ID="m_tbItemQuantity"
                  runat="server"></asp:TextBox>            
            </td>
          </tr>
          <tr>
            <td style="width:25%">
                <asp:Button ID="m_btSubmit" runat="server" Text="Submit" />
            </td>
            <td style="width:75%">
            </td>
          </tr>
          <tr>
            <td style="width:25%">
                <asp:Label ID="m_lbOutput" runat="server"
                  Text=""></asp:Label>
            </td>
            <td style="width:75%">
            </td>
          </tr>                              
        </table>
    </div>
    </form>
</body>
</html>

图 10 衬于代码下方 OrderRequest_EntryPage.aspx 页。

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Data.SqlClient;


public partial class OrderRequest_EntryPage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    protected void m_btSubmit_Click(object sender, EventArgs e)
    {
        using (SqlConnection objConnection = new SqlConnection())
        {
            objConnection.ConnectionString = "Data Source=localhost;Initial
              Catalog=ServiceBrokerDB_Test;Integrated Security=True";
            using (SqlCommand objCmd = new SqlCommand())
            {
                objCmd.Connection = objConnection;
                objCmd.CommandType = CommandType.StoredProcedure;
                objCmd.CommandText = "spEnterOrderRequest";

                SqlParameter objOutputParam = 
                  objCmd.Parameters.Add("@nOrderID",SqlDbType.Int);
                objOutputParam.Direction = ParameterDirection.Output;
                objCmd.Parameters.Add("@nCustomer",SqlDbType.Int).Value = 
                  Convert.ToInt32(this.m_tbCustomerID.Text) ;
                objCmd.Parameters.Add("@nItemNumber", SqlDbType.Int).Value = 
                  Convert.ToInt32(this.m_tbItemNumber.Text);
                objCmd.Parameters.Add("@nItemQuantity", SqlDbType.Int).Value
                  = Convert.ToInt32(this.m_tbItemQuantity.Text);


                try
                {
                    objConnection.Open();
                    objCmd.ExecuteNonQuery();

                    this.m_lbOutput.Text = "The Order Identifier is " + 
                      objOutputParam.Value;
                }
                catch (SqlException ex)
                {
                    this.m_lbOutput.Text = "Exception: " + ex.Message;
                }
                finally
                {
                    objConnection.Close();
                }
            }
        }
    }
}

下一步是实现 OrderStatus_Page.aspx,其中包含一个标签 Web 控件 (Label1) 和一个文本框 Web 控件 (m_tbOrderID) 以获取该订单标识符和一个按钮 Web 控件 (m_btSubmit) 以便请求该订单的状态。 状态显示使用一个最后输出标签 Web 控件 (m_lbOutput)。 在网页代码所示图 11 和代码隐藏的图 12

图 11 OrderStatus_Page.aspx 的布局。

<%@ Page Language="C#" AutoEventWireup="true" 
CodeFile="OrderStatus_Page.aspx.cs" Inherits="OrderStatus_Page" %>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Order Status page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <table border="0" width="100%" cellpadding="0" cellspacing="0">
          <tr>
            <td style="width:25%">
                <asp:Label ID="Label1" runat="server" Text="Enter
                  OrderID"></asp:Label>            
            </td>
            <td style="width:75%">
                <asp:TextBox ID="m_tbOrderID" runat="server"></asp:TextBox>
            </td>
          </tr>
          <tr>
            <td style="width:25%">
                <asp:Button ID="m_btSubmit" runat="server" Text="Submit" 
                  OnClick="m_btSubmit_Click" />
            </td>
            <td style="width:75%">
            </td>
          </tr>          
          <tr>
            <td style="width:25%">
                <asp:Label ID="m_lbOutput" runat="server" 
                  Text=""></asp:Label>
            </td>
            <td style="width:75%">
            </td>
          </tr>                                       
        </table>    
    </div>
    </form>
</body>
</html>

图 12 衬于代码下方 OrderStatus_Page.aspx 页

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Data.SqlClient;


public partial class OrderStatus_Page : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {


    }
    protected void m_btSubmit_Click(object sender, EventArgs e)
    {
        using (SqlConnection objConnection = new SqlConnection())
        {
            objConnection.ConnectionString = "Data Source=localhost;
Initial Catalog=ServiceBrokerDB_Test;Integrated Security=True";
            using (SqlCommand objCmd = new SqlCommand())
            {
                objCmd.Connection = objConnection;
                objCmd.CommandType = CommandType.Text;
                objCmd.CommandText = "select vcOrderStatusDescription " +
                                     "from tbOrder " +
                                     "where nOrderID=@nOrderID";


                objCmd.Parameters.Add
("@nOrderID", SqlDbType.Int).Value = Convert.ToInt32(this.m_tbOrderID.Text);
                try
                {
                    objConnection.Open();
                    SqlDataReader objReader = objCmd.ExecuteReader();
                    objReader.Read();
                    string strStatusDescription = objReader.GetString(0);


                    this.m_lbOutput.Text = "The Order Status is " + strStatusDescription;
                }
                catch (SqlException ex)
                {
                    this.m_lbOutput.Text = "Exception: " + ex.Message;
                }
                finally
                {
                    objConnection.Close();
                }
            }
        }
    }
}

与我 ’ve 描述的示例,现在您有一个模板创建可靠的、 分布式消息使用 SQL Server 2005 Service Broker 的技术解决方案。您应该能够并且适应本文已经以适合您自己的业务方案的示例。

Juan Carlos Olamendy Turruellas* 我一个高级的集成解决方案架构师和顾问。其主要的重点是面向对象的分析和设计、 数据库设计、 企业应用程序集成,统一建模语言、 设计模式,企业应用程序的结构和软件开发过程使用灵活的方法。 他的奖励的最有价值的专业状态,在 2007年、 2008,和 2009,以及 Oracle ACE 2008 中的状态。您可以在 johnx_olam@fastmail.fm联系 Juan。*