几周以来,我一直在与 Service Broker 合作,在跟踪对话时,我发现了一些奇怪的东西……对话最后似乎在队列中有一条额外的消息,这也导致了错误的发生。
据我了解,对话对话框应如下所示:
- 触发器触发和呼叫开始对话
- 发起者服务将消息放入队列
- 目标服务触发存储过程,接收消息并结束对话
- 发起者服务触发一个存储过程,接收消息并结束对话。
发生的事情是在队列末尾有一条额外的消息再次触发存储的过程,但详细信息(即conversation_handle)为空。它还会引发错误:Conversion failed when converting from a character string to uniqueidentifier.  为了避免错误,我将 conversation_handle 转换为 varchar,然后检查是否为 null。对我来说似乎很愚蠢,我必须这样做。
更新:错误已经消失 - 我相信它是在我尝试记录会话句柄(它是空的)时发生的。
结束对话而不得到额外消息的正确方法是什么?
这是我现在拥有的:
alter proc dbo.MessageProcessor
as
begin
    set nocount on;
    set xact_abort on;
    declare @xactState smallint
    declare @handle uniqueidentifier, 
            @responseXml xml, 
            @messageType sysname;
    begin transaction;
    begin try
        ;receive top(1)
            @messageType = message_type_name, 
            @handle = conversation_handle, 
            @responseXml = message_body
        from dbo.MessageQueue
        if(@handle is not null)
        begin
            if (@messageType = N'DEFAULT')
            begin
                save transaction MessageProcessor_Tran
                begin try
                    -- doing work here
                end try
                begin catch
                    select @xactState = xact_state()
                    if(@xactState = -1)
                    begin
                        rollback;
                        raiserror(N'Unrecoverable error', 16, 1)
                    end
                    else if(@xactState = 1)
                    begin
                        rollback transaction MessageProcessor_tran
                    end
                    -- log error information
                end catch
            end 
            else if (@messageType = N'http://schemas.microsoft.com/SQL/ServiceBroker/Error')
            begin
                declare @errorNumber int,
                        @errorMessage nvarchar(4000);
                with xmlnamespaces (DEFAULT N'http://schemas.microsoft.com/SQL/ServiceBroker/Error')
                select @errorNumber = @responseXml.value ('(/Error/Code)[1]', 'INT'),
                    @errorMessage = @responseXml.value ('(/Error/Description)[1]', 'NVARCHAR(4000)');
                -- log error
            end
            end conversation @handle
            set @handle = null
        end
        commit
    end try
    begin catch
        declare @error int, 
                @message nvarchar(2048)
        select @error = error_number(), 
            @message = error_message(), 
            @xactState = xact_state();
        if(@xactState <> 0)
            rollback;
        if(@handle is not null)
            end conversation @handle;
        -- log error            
        raiserror(N'Error: %i, %s', 1, 60, @error, @message) with log;
    end catch
end
go