Current .Net Version: 2.0.50727
Home
Articles
C# & .Net Framework Reference
Practice C# Online
XML Web Services
RSS Feeds
Code Snippets
T-SQL Scripts
Videos
Valuable Links
Contact Us
Mike's C#, VS.NET and SQL Blog




ROR XML Info.
Service Broker - Message Types
Author: Michael G.     Last Updated:12/17/2004 4:05:00 PM
Category(ies): SQL Server 2005
Description: An explanation on types of messages allowed by Service Broker.
  Add & View Comments About this Article

Tested with .Net 2.0.40607.42 and SQL Server 2005 9.00.852

  • Service Broker Architecture
  • Service Broker Conversation Architecture
  • Service Broker Message Types
  • Service Broker Contracts
  • Service Broker Links
  • Service Broker Queues
  • Service Broker Services
  • Service Broker Service Programs
  • Service Broker Dialogs
  • < Back | Next >

  • What are Message Types?
  • Creating Message Types
  • Message Type Related Exceptions and Dialogs
  • Locating Message Types
  • Message Type Body

  • Service Broker Message Types #1

    What are Message Types?

    Messages sent to and from Service Broker Services (or
    Conversation Endpoints) must conform to a particular Message Type and Contract. Message Types define the type of data the body of a message must contain (or not contain). Contracts define who can send messages (the initiating or target Conversation Endpoint) of a particular Message Type. Each contract must contain at least one Message Type. So, when two services send messages to each other, they do so by agreeing to communicate via the terms of a Contract, which contains rules regarding which Message Types can be sent from one Conversation Endpoint to another. Always remember that every message is tied to exactly one Message Type. So, you can use the term Message Type and Message interchangeably. Dialogs, on the other hand, are tied to exactly one Contract. Messages themselves are not tied to a particular Contract. Rather, it's the Dialog Conversations that they are sent through that are tied to Contracts.

    Message Types are validated against when they're received by a Service Broker Service. Validation, in this context, refers to validating the type of data a message's body contains (or does not contain). If the incoming message (at the target) does not conform to the contract's allowable message types, an error message is generated (where and what type of error message varies considerably and depends on many factors; explained below). In other words, not only must the message's body pass the Message Type's validation requirements, but it must also be one of the Contract's allowable Message Types. Each message sent must conform to one Message Type and that Message Type must be part of a matching Contract. Contracts are tied to dialogs and each message sent is done so through the means of a Dialog Conversation.

    There are four types of Message Type validation:
    • None - any type of message body is acceptable.
    • Empty - only an empty message body is acceptable.
    • Well Formed XML - only a message body with valid XML is acceptable.
    • Well Formed XML with Schema Collection - only a message with valid XML and XML schema is acceptable.


    Creating Message Types: Syntax: CREATE MESSAGE TYPE message_type_name [ AUTHORIZATION owner_name ] [ VALIDATION = { NONE | EMPTY | WELL_FORMED_XML | VALID_XML WITH SCHEMA COLLECTION schema_collection_name } ] [ ; ] Example: CREATE MESSAGE TYPE DotNetFunMessageTypeValidateXML VALIDATION = WELL_FORMED_XML; CREATE MESSAGE TYPE DotNetFunMessageTypeValidateEmpty VALIDATION = EMPTY; CREATE MESSAGE TYPE DotNetFunMessageTypeValidateNone VALIDATION = NONE; The Message Types of Messages are validated at the target Endpoint's Service and not the initiating Service's Endpoint. However, the initiating Endpoint's Service will first check the local database for a matching Message Type name and throw an exception if none is found. Matching Message Type names and validation are checked differently at the initiating Endpoint's Service than they are at the target Endpoint's Service. At the initiating side (Conversation Endpoint), only the existence of a matching Message Type name is checked. At the target side, both a matching Message Type name and message body validation are checked.

    While the Message Type name must match exactly at both the initiating side and the target side, their validation types don't have to be exact, they just have to be compatible. You can have two compatible Message Types with two different validation types and be able to send messages without exceptions/errors. For example, you will be able to send a message who's Message Type is Empty and the target's matching Message Type is None. This is possible because None allows any type of message body in a message.

    Example, Sending a Message of Message Type Empty to Another Service with a Different, but Compatible Message Type: -- Sending Message Type with Validation = Empty -- to Another Service with a Validation = None -- Initiator Service (DotNetFunService) -- Let's change the local/initiator's Message Type: ALTER DotNetFunMessageTypeValidateNone VALIDATION = EMPTY; DECLARE @DialogHandle UNIQUEIDENTIFIER, @Instance UNIQUEIDENTIFIER SELECT @Instance = service_broker_guid FROM sys.databases WHERE database_id = DB_ID('DotNetFun2'); BEGIN DIALOG CONVERSATION @DialogHandle FROM SERVICE DotNetFunService TO SERVICE 'DotNetFun2Service', @Instance ON CONTRACT DotNetFunContractSentByAny; SEND ON CONVERSATION @DialogHandle MESSAGE TYPE DotNetFunMessageTypeValidateNone ('Oops, we''re not suppose to have anything in the body!'); END CONVERSATION @DialogHandle; /* No error will occur, even if the body violates the DotNetFunMessageTypeValidateNone Message Type's Empty requirement, because the target Message Type, DotNetFunMessageTypeValidateNone, is defined with VALIDATION = NONE, which means any kind of body, even empty ones, are acceptable. */ So, in order for a message to be accepted at the target Endpoint's service, three conditions must be satisfied that pertain to the Message Type: 1) the initiator and target's Message Type names must be exactly the same, 2) the message must pass validation against its message body (the type of data it contains, or does not contain) and 3) the Message Type used must be allowed by the Dialog conversation's assigned Contract.

    Message Type Related Exceptions and Dialogs

    Special consideration should be taken when dealing with Message Type related exceptions and
    Dialogs. When you send messages, you do so as part of an existing Dialog Conversation: you create a dialog and then send one or messages through that dialog. When a Message Type related exception occurs, the state of the dialog is affected. After an exception is encountered, any further attempts to send a message through the dialog are prevented, and this applies to both conversation endpoints of the dialog (initiator and target).

    In a non-transaction scenario, if you send three messages and the first contains a body that violates a Message Type's requirements, an exception will not get thrown until you attempt to send the second message. That's because by the time you reach the second message, the dialog on which the first message was sent has been placed into an error state. So, when you attempt to send the second message, this state is recognized and an exception is thrown accordingly.

    If you send three messages and the second contains a body that violates a Message Type's requirements, an exception will not get thrown until you attempt to send the third message. However, since the first message did not violate its assigned Message Type requirements, it will arrive at the target Endpoint's service. Also, an exception related error message, of message type http://schemas.microsoft.com/SQL/ServiceBroker/Error will also be sent to the target Endpoint's service automatically. Both the successful message and the error message will belong to this type of Dialog Conversation.

    In a non-transaction scenario, if a message fails validation, an error message of type http://schemas.microsoft.com/SQL/ServiceBroker/Error will also get added to the initiator's queue if the dialog to which it belongs has not already been explicitly ended. When Service Broker detects an error, it doesn't end the dialog, it simply puts it in an error state, and it's up to you to explicitly end the dialog conversation - either from the initiating Endpoint's service or the target Endpoint's service (and only if a message was sent to the target Endpoint to begin with). Messages in queues will remain there if 1) their associated dialogs have not been explicitly ended, or 2) you RECEIVE the messages (and the queue's retention setting is OFF).

    It's important to recognize how different scenarios might bring about different behaviors when it comes to Message Type related exceptions/errors. Here are three as an example:
    Service Broker Message Types Exception Scenario A

    Service Broker Message Types Exception Scenario B

    Service Broker Message Types Exception Scenario C

    Service Broker Message Types Exception Scenario D

    In Scenario A, no messages get sent to the target Endpoint's Service because there are no previous messages that pass validation. In Scenario B, only the first valid message gets sent to the target while the rest throw exceptions. In Scenario C, we have the exact same situation as Scenario A, except for the fact that the error message that was automatically generated by Service Broker is retained in the initiator's queue because the dialog to which it belongs was not explicitly ended.

    Please take note that scenarios A, B and C make use of no explicit transactions. This means that each statement is implicitly enlisted within its own transaction. Therefore, any one exception will not affect the entire script of statements as a whole. I do not recommend using non-transaction based approaches. Rather, use an all-or-nothing approach by enlisting all messages in a single transaction. That way, if an error occurs anywhere, no messages get sent to the target. I wouldn't want some messages to get sent and some not. Scenario D illustrates this approach.

    In order to accurately determine if an error occured for a given dialog, query the sys.conversation_endpoints catalog view for that dialog and evaluate the state and/or state_desc columns. Make sure you query the view BEFORE the dialog conversation is ended (either explicitly or implicitly).Otherwise, once a dialog conversation is ended, the view no longer contains a reference to the dialog. If at least one message is sent to the target Endpoint's Service, as in Scenario B above, a record will exist in sys.conversation_endpoints in the target Endpoint's database for the same dialog conversation. Dialogs can be tracked across Service Broker service boundries via the conversation_id value. You won't be able to determine if an individual message has failed validation. Rather, you will only be able to tell if the dialog, as a whole, has failed - and even one or more messages may have gotten through to the target in that case (unless all messages were rolled back as part of a transaction). If the Initiating Endpoint and Target Endpoint are using the same Service, there will exist two sys.conversation_endpoints records: one for the Initiating Endpoint and one for the Target Endpoint.

    I'll be discussing more about exception handling in a another article, but for now, just remember that an exception may not always be thrown when a message does not pass its Message Type validation requirements.

    More About Conversation Endpoints Here.

    Example, Detecting Dialog Errors (used Scenario D pattern) DECLARE @DialogHandle uniqueidentifier, @BrokerGUID uniqueidentifier, @ConvID uniqueidentifier; SELECT @BrokerGUID = service_broker_guid FROM sys.databases WHERE database_id = DB_ID('DotNetFun2'); BEGIN TRAN BEGIN TRY BEGIN DIALOG CONVERSATION @DialogHandle FROM SERVICE DotNetFunService TO SERVICE 'DotNetFun2Service', @BrokerGUID ON CONTRACT DotNetFunContractSentByAny; SEND ON CONVERSATION @DialogHandle MESSAGE TYPE DotNetFunMessageTypeValidateEmpty ('Oops, the body needs to empty!'); -- Retrieve the Conversation ID - which is unique across Service -- boundries for each dialog (we don't need it; we can use -- the conversation_handle; however, conversation_id will be -- the same at the target for this dialog - if any messages -- arrived there): SELECT @ConvID = conversation_id FROM DotNetFun.sys.conversation_endpoints WHERE conversation_handle = @DialogHandle; -- If the dialog is in an error state, throw an actual exception, -- or do something else: IF EXISTS(SELECT * FROM DotNetFun.sys.conversation_endpoints WHERE state = 'ER' AND conversation_id = @ConvID) RAISERROR('A dialog error has occured....', 1, 127); ELSE BEGIN -- Make sure you end the conversation AFTER looking for error -- conditions; otherwise, those conditions/records will not -- exist for this dialog once you end it accordingly; or, -- don't end it at all - the point is to not end the dialog -- before checking for the error conditions: END CONVERSATION @DialogHandle; COMMIT TRAN; END END TRY BEGIN CATCH ROLLBACK TRAN; DECLARE @ERRMSG VARCHAR(MAX); SET @ERRMSG = ERROR_MESSAGE(); RAISERROR(@ERRMSG, 18, 127); END CATCH

    Locating Message Types

    You can tell what Message Types exist for a given database by querying the sys.conversation_message_types catalog view or by navigating to the database's Service Broker folder in Object Explorer within SQL Server Management Studio.
    sys.conversation_message_types schema:
    • name - sysname; Name of message type, unique within the database.
    • message_type_id - int; Identifier of the message type, unique within the database.
    • principal_id - int; Identifier for the database principal that owns this message type.
    • validation - char; Validation done by Broker prior to sending messages of this type: N (None), X (XML) or E (Empty).
    • validation_desc - nvarchar(60); Description of the validation done by Broker prior to sending messages of this type: NONE, EMPTY or XML.
    • xml_collection_id - int; For validation that uses an XML schema, the identifier for the schema collection used.

    Message Type Body

    The body of a message is stored in the form of a scalar (single variable) varbinary(max) (variable binary with an allowable maximum size) data type. That means that any type of data that can be converted to varbinary(max) can be stored in the body of a message. By default, if you don't convert the message body to varbinary(max), SQL Server will do it for you.

    Example, Sending an XML Table from a Service Broker Service and Receiving it at Another: /* ----------- FROM THE INITIATOR SERVICE ----------- */ USE DotNetFun; -- Set variables: DECLARE @DialogHandle uniqueidentifier, @BrokerGUID uniqueidentifier, @ConvID uniqueidentifier, @BodyXML xml; -- Set the target database instance identifier: SELECT @BrokerGUID = service_broker_guid FROM sys.databases WHERE database_id = DB_ID('DotNetFun2'); BEGIN TRAN BEGIN TRY -- Start a dialog conversation: BEGIN DIALOG CONVERSATION @DialogHandle FROM SERVICE DotNetFunService TO SERVICE 'DotNetFun2Service', @BrokerGUID ON CONTRACT DotNetFunContractSentByAny; -- Retrieve table contents as XML and send it: SELECT @BodyXML = (SELECT * FROM sys.service_message_types FOR XML AUTO, XMLSCHEMA); SEND ON CONVERSATION @DialogHandle MESSAGE TYPE DotNetFunMessageTypeValidateXML (@BodyXML); SELECT @ConvID = conversation_id FROM sys.conversation_endpoints WHERE conversation_handle = @DialogHandle; IF EXISTS(SELECT * FROM sys.conversation_endpoints WHERE state = 'ER' AND conversation_id = @ConvID) RAISERROR('Dialog is in an error state.', 18, 127); ELSE BEGIN -- End the conversation (one-way communication): END CONVERSATION @DialogHandle; COMMIT TRAN; END END TRY BEGIN CATCH -- Discard all messages - do not send or save any ROLLBACK TRAN; DECLARE @ERRMSG VARCHAR(MAX); SET @ERRMSG = ERROR_MESSAGE(); RAISERROR(@ERRMSG, 18, 127); END CATCH GO /* ----------- FROM THE TARGET SERVICE ----------- */ USE DotNetFun2; -- Set variables: DECLARE @Queue TABLE (DialogHandle UNIQUEIDENTIFIER, MessageBody VARBINARY(MAX), MessageTypeName SYSNAME); DECLARE @DialogHandle UNIQUEIDENTIFIER, @MessageBody VARBINARY(MAX), @MessageTypeName SYSNAME, @BodyXML XML; -- Iterate through each conversation group of messages: WHILE 1 = 1 BEGIN WAITFOR ( RECEIVE conversation_handle, message_body, message_type_name FROM DotNetFun2..DotNetFun2Queue INTO @Queue), TIMEOUT 5000; --&gt; wait 5 seconds -- If no messages exist, let's break out of the loop: IF NOT EXISTS(SELECT * FROM @Queue) BREAK; -- Let's go through each message in the in-process table: DECLARE cursor_DotNetFun CURSOR FAST_FORWARD FOR SELECT * FROM @Queue; OPEN cursor_DotNetFun; FETCH NEXT FROM cursor_DotNetFun INTO @DialogHandle, @MessageBody, @MessageTypeName; WHILE @@FETCH_STATUS = 0 BEGIN -- If this message matches the XML Message Type, let's -- process it accordingly: IF @MessageTypeName = 'DotNetFunMessageTypeValidateXML' BEGIN -- Let's retrieve the binary body and convert it -- back to XML: SET @BodyXML = CAST(@MessageBody AS XML); -- Do something with the XML data here... PRINT CAST(@BodyXML AS VARCHAR(MAX)); END -- Let's check for errors too: IF @MessageTypeName = 'http://schemas.microsoft.com/SQL/ServiceBroker/Error' BEGIN -- The message body of this type of Message Type -- is XML. -- Do something about the error here... PRINT 'An error occured: ' + CAST(CAST(@MessageBody AS XML)AS VARCHAR(MAX)); END -- Check all other message types here... FETCH NEXT FROM cursor_DotNetFun INTO @DialogHandle, @MessageBody, @MessageTypeName; END CLOSE cursor_DotNetFun; DEALLOCATE cursor_DotNetFun; -- Once we're done, let's purge the in-process table: DELETE FROM @Queue; END;

    Comments Click Here to Add your Comment
    No Comments Found.
     
    COPYRIGHT/DISCLAIMER | CONTACT US