17.2. Programming Service BrokerSMO Service Broker classes are used to manage Service Broker objects programmatically. SMO Service Broker classes do not support creating conversations or sending and receiving messages. You send and receive streams of asynchronous messages using T-SQL DML statements, which are discussed in the "Implementing a Service Broker Service" section later in this chapter. Figure 17-2 shows the relationship between SMO classes for Service Broker programming . Figure 17-1. Service Broker message flowThe remainder of this chapter contains examples that show how to use the SMO Service Broker classes and provides descriptions of the classes. You need a reference to the following assemblies to compile and run the examples:
The ServiceBroker object described in is the top-level class in the SMO Service Broker class hierarchy and represents the implementation of Service Broker on a SQL Server database. The ServiceBroker property of the Database class returns the Service Broker implementation on a database. 17.2.1. Enumerating Service Broker ObjectsThis example enumerates all Service Broker objectsmessage types, contracts, queues, services, routes, and remote service bindings: using System; using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlServer.Management.Common; using Microsoft.SqlServer.Management.Smo.Broker; class Program Figure 17-2. Service Broker class relationships{ static void Main(string[] args) { Server server = new Server("localhost"); Database db = server.Databases["AdventureWorks"]; ServiceBroker sb = db.ServiceBroker; Console.WriteLine("MESSAGE TYPES:"); foreach (MessageType mt in sb.MessageTypes) Console.WriteLine(" " + mt.Name); Console.WriteLine(Environment.NewLine + "CONTRACTS:"); foreach (ServiceContract sc in sb.ServiceContracts) Console.WriteLine(sc.Name); Console.WriteLine(Environment.NewLine + "QUEUES:"); foreach (ServiceQueue sq in sb.Queues) Console.WriteLine(" " + sq.Name); Console.WriteLine(Environment.NewLine + "SERVICES:"); foreach (BrokerService bs in sb.Services) Console.WriteLine(" " + bs.Name); Console.WriteLine(Environment.NewLine + "ROUTES:"); foreach (ServiceRoute sr in sb.Routes) Console.WriteLine(" " + sr.Name); Console.WriteLine(Environment.NewLine + "REMOTE SERVICE BINDINGS:"); foreach (RemoteServiceBinding rsb in sb.RemoteServiceBindings) Console.WriteLine(" " + rsb.Name); Console.WriteLine(Environment.NewLine + "Press any key to continue."); Console.ReadKey( ); } } Results are shown in Figure 17-3. The ServiceBroker class exposes a set of collections of the following Service Broker objects: message types, contracts, queues, services, routes, and remote service bindings. A discussion of the classes used to programmatically manage these objects follows. The classes used to manage Server Broker message types are described in Table 17-1. Figure 17-3. Results for enumerating Service Broker objects example
The classes used to manage Server Broker service contracts programmatically are described in Table 17-2.
The classes used to manage Server Broker message queues programmatically are described in Table 17-3.
The classes used to programmatically manage Server Broker services are described in Table 17-4.
The SMO classes used to manage Server Broker routes programmatically are described in Table 17-5.
The SMO classes used to manage Server Broker remote service bindings programmatically are described in Table 17-6. 17.2.2. Implementing a Service Broker ServiceThis example sets up Service Broker objects used in the examples later in this chapter. The following objects are created:
The source code follows: using System; using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlServer.Management.Common; using Microsoft.SqlServer.Management.Smo.Broker; class Program { static void Main(string[] args) { Server server = new Server("localhost"); Database db = server.Databases["ProgrammingSqlServer2005"]; ServiceBroker sb = db.ServiceBroker; // create the request and response message types MessageType requestMessage = new MessageType(sb, "HelloWorldRequest"); requestMessage.MessageTypeValidation = MessageTypeValidation.Xml; requestMessage.Create( ); MessageType responseMessage = new MessageType(sb, "HelloWorldResponse"); responseMessage.MessageTypeValidation = MessageTypeValidation.Xml; responseMessage.Create( ); // create the service contract ServiceContract contract = new ServiceContract(sb, "HelloWorldContract"); contract.MessageTypeMappings.Add(new MessageTypeMapping( contract, "HelloWorldRequest", MessageSource.Initiator)); contract.MessageTypeMappings.Add(new MessageTypeMapping( contract, "HelloWorldResponse", MessageSource.Target)); contract.Create( ); // create the queues ServiceQueue initiatorQueue = new ServiceQueue(sb, "HelloWorldInitiator"); initiatorQueue.Create( ); ServiceQueue targetQueue = new ServiceQueue(sb, "HelloWorldTarget"); targetQueue.Create( ); // create the services BrokerService requestService = new BrokerService(sb, "HelloWorldRequestService"); requestService.QueueName = "HelloWorldTarget"; requestService.ServiceContractMappings.Add( new ServiceContractMapping(requestService, "HelloWorldContract")); requestService.Create( ); BrokerService responseService = new BrokerService(sb, "HelloWorldResponseService"); responseService.QueueName = "HelloWorldInitiator"; responseService.ServiceContractMappings.Add( new ServiceContractMapping(responseService, "HelloWorldContract")); responseService.Create( ); Console.WriteLine("Press any key to continue."); Console.ReadKey( ); } } You can view the newly created objects in Object Explorer in SQL Server Management Studio by opening the Databases ProgrammingSqlServer2005 Service Broker node. This example does not specify all of the possible properties when creating the objects. For example, the message owner is not specified and defaults to the Windows account. The rest of the discussion for this example describes a generic approach for creating the different objects. Creating a message type defines the name of the message, the owner of the message type as a database user or role, and how the message is validated as a value from the MessageTypeValidation enumeration, as described in Table 17-7.
Creating a contract defines the name of the contract, the owner of the contract as a database user or role, message types included in the contract, and which endpoints (MessageSource.Initiator, MessageSource.InitiatorAndTarget, or MessageSource.Target) can send each message type. Creating a queue defines the name of the queue, the status (indicating whether the queue is available for use), the retention (specifying whether messages are removed from the queue once retrieved), and, optionally, a stored procedure that runs when a message arrives on the queue, to process the message automatically. Creating a service defines the name of the service, the owner of the service as a database user or role, the queue that receives messages for the service, and the name of one or more contracts for which the service is a target. The service can initiate conversations only if no contracts are specified. Creating a route defines the name of the route, the owner of the route as a database user or role, the name of the remote service that the route points to, the database that hosts the target service, the length of time that SQL Server retains the route in the routing table, and network addresses for the route. Additionally, the other object you can create is a remote service binding. Creating a remote service binding defines the name of the remote service binding, the owner of the binding as a database user or role, the database principal that owns the certificate associated with the remote service, and the remote service to bind to the user. The preceding example corresponds to the following T-SQL DML batch: USE [ProgrammingSqlServer2005] GO CREATE MESSAGE TYPE [HelloWorldRequest] VALIDATION = WELL_FORMED_XML CREATE MESSAGE TYPE [HelloWorldResponse] VALIDATION = WELL_FORMED_XML CREATE CONTRACT [HelloWorldContract] ( [HelloWorldRequest] SENT BY INITIATOR, [HelloWorldResponse] SENT BY TARGET ) CREATE QUEUE [HelloWorldInitiator] CREATE QUEUE [HelloWorldTarget] CREATE SERVICE [HelloWorldRequestService] ON QUEUE [HelloWorldTarget] ( [HelloWorldContract] ) CREATE SERVICE [HelloWorldResponseService] ON QUEUE [HelloWorldInitiator] ( [HelloWorldContract] ) The following T-SQL batch starts a conversation from the HelloWorldRequestService service to the HelloWorldResponseService service by using the HelloWorldContract and sends a message of type HelloWorldRequest by using the conversation: BEGIN TRANSACTION DECLARE @conversationHandle uniqueidentifier BEGIN DIALOG @conversationHandle FROM SERVICE [HelloWorldRequestService] TO SERVICE 'HelloWorldResponseService' ON CONTRACT [HelloWorldContract] WITH ENCRYPTION = OFF; SEND ON CONVERSATION @conversationHandle MESSAGE TYPE [HelloWorldRequest] ( CAST(N'<Request>Hello world request</Request>' AS XML) ) COMMIT The BEGIN DIALOG T-SQL statement starts a conversation between two services. The new converstation is assigned a system-generated conversation handle with a data type of uniqueidentifier. All messages are part of a conversation. You can specify a conversation group when starting a conversation. If one is not specified, SQL Server automatically creates a new conversation group for the new conversation. The SEND T-SQL statement sends a message to a service using an existing conversationthe conversation that the message belongs to is identified by a conversation handle such as the one returned by the BEGIN DIALOG statement in the preceding example. If the SEND statement is not the first statement in a batch, you must terminate the preceding T-SQL statement with a semicolon (;). The following T-SQL batch retrieves the first message from the HelloWorldInitiator queue. If the message type name is HelloWorldRequest, a response is sent (again using a SEND T-SQL statement) as part of the conversation initiated in the preceding example and the conversation is ended. DECLARE @conversationHandle uniqueidentifier DECLARE @message_body nvarchar(MAX) DECLARE @message_type_name sysname; BEGIN TRANSACTION; RECEIVE TOP(1) @message_type_name = message_type_name, @conversationHandle = conversation_handle, @message_body = message_body FROM [HelloWorldInitiator] IF @message_type_name = 'HelloWorldRequest' BEGIN SEND ON CONVERSATION @conversationHandle MESSAGE TYPE [HelloWorldResponse] ( CAST(N'<Response>Hello world response</Response>' AS XML) ); END CONVERSATION @conversationHandle; END COMMIT The RECEIVE T-SQL statement retrieves one or more messages from a message queue. A RECEIVE statement can specify a conversation handle or conversation group ID to retrieve specific messages. The RECEIVE statement has an optional WAITFOR clause that specifies the length of time to wait for a message. The RECEIVE statement removes the message from the queue unless the RETENTION property of the queue is set to on. If the RECEIVE statement is not the first statement in a batch, you must terminate the preceding statement with a semicolon. The END CONVERSATION T-SQL statement ends one side of an existing conversation. A conversation ends when initiator and target both end the conversation or when the conversation expires, specified by the LIFETIME argument in the BEGIN DIALOG T-SQL statement. When a conversation ends, Service Broker removes all messages for the conversation from the service queue. You can use the BEGIN CONVERSATION TIMER T-SQL statement to start a timer. When the timer expires, a message of the type http://schemas.Microsoft.com/SQL/ServiceBroker/Messages/DialogTimer is put on the local queue for the conversation. Each side of the conversation has its own conversation timer. You can also use the GET trANSMISSION STATUS T-SQL statement to return a description of the last transmission error for one side of a conversation. An empty string is returned if the last transmission succeeded. You can see the message by querying the HelloWorldTarget queue, using a SELECT T-SQL statement: SELECT * FROM HelloWorldTarget Service Broker manages the contents of the queue. So, although you can issue SELECT statements to query the contents of the queue, the queue cannot be the target of an INSERT, UPDATE, or DELETE statement. This T-SQL batch retrieves a response message from the HelloWorldTarget queue, outputs the response, and ends the conversation: DECLARE @conversationHandle uniqueidentifier DECLARE @message_body nvarchar(MAX) DECLARE @message_type_name sysname; BEGIN TRANSACTION; RECEIVE TOP(1) @message_type_name = message_type_name, @conversationHandle = conversation_handle, @message_body = message_body FROM [HelloWorldTarget] IF @message_type_name = 'HelloWorldResponse' BEGIN PRINT @message_type_name; PRINT @conversationHandle; PRINT @message_body; END CONVERSATION @conversationHandle; END COMMIT Results are shown in Figure 17-4. Figure 17-4. Results for retrieve response example |