Оператор Select Получает byte [] и строку, но не XML - PullRequest
2 голосов
/ 17 июля 2009

В приложении C # я извлекаю сообщение из очереди брокера служб SQL с помощью следующего оператора.

При попытке преобразовать message_body в SqlBytes и другие типы выдается исключение. Во время выполнения message_body всегда кажется начинающимся как type byte [].

Оставление message_body в качестве байта [] работает, но я получаю исключение с жалобой на ошибку в моем XML-документе при попытке десериализации типа SccmAction.

Моя конечная цель - десериализовать message_body в любой форме в интерфейс моего приложения.

RECEIVE TOP (1)
        conversation_handle as ConversationHandle
       ,message_body
       ,message_body as MessageBody
       ,convert(xml, message_body) as MessageBodyXml
   FROM [dbo].[MY_QUEUE_SubmitQueue]

using (SqlConnection connection =
    new SqlConnection(ConnectionString))
{
    SqlCommand command = new SqlCommand(ReceiveString, connection);
    connection.Open();

    SqlDataReader reader = command.ExecuteReader();

    while (reader.Read())
    {
        SqlBytes sb = (SqlBytes)reader["message_body"];
        SccmAction sa = DeserializeObject<SccmAction>(sb);
        IDelivery iD = DeserializeObject<IDelivery>(sb);
    }

    reader.Close();
}

public static T DeserializeObject<T>(byte[] ba)
{
    XmlSerializer xs = new XmlSerializer(typeof(T));
    MemoryStream memoryStream = new MemoryStream(ba);
    XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
    return (T)xs.Deserialize(memoryStream);
}

<SccmAction>
  <MachineName>Godzilla</MachineName>
  <CollectionName>cn324321</CollectionName>
  <Action>Install</Action>
  <EntryDateTime>Jul 17 2009 12:15PM</EntryDateTime>
</SccmAction>

Редактировать: Дополнительная информация

Определение очереди

CREATE MESSAGE TYPE [MY_MSG_MessageType] AUTHORIZATION 
[dbo] VALIDATION = WELL_FORMED_XML 

CREATE CONTRACT [MY_CONTRACT_Contract] AUTHORIZATION [dbo] 
([MY_MSG_MessageType] SENT BY INITIATOR) 

CREATE QUEUE [dbo].[MY_QUEUE_SubmitQueue] WITH STATUS = ON , 
RETENTION = OFF ON [PRIMARY] 

CREATE QUEUE [dbo].[MY_QUEUE_ResponseQueue] WITH STATUS = ON , 
RETENTION = OFF ON [PRIMARY] 

CREATE SERVICE [MY_QUEUE_SubmitService] AUTHORIZATION [dbo] 
ON QUEUE [dbo].MY_QUEUE_SubmitQueue([MY_CONTRACT_Contract]) 

CREATE SERVICE [MY_QUEUE_ResponseService] AUTHORIZATION [dbo] ON 
QUEUE [dbo].[MY_QUEUE_ResponseQueue]([DEFAULT])

Исключения

Type of value has a mismatch with column type Couldn't store
<<SccmAction> 
<MachineName>Godzilla</MachineName><CollectionName>cn324321</CollectionName> 
<Action>Install</Action> 
<EntryDateTime>Jul 17 2009 12:15PM</EntryDateTime> 
</SccmAction>> in MessageBodyXml Column. Expected type is SqlXml.

Невозможно привести объект типа 'System.Byte []' для ввода 'System.Data.SqlTypes.SqlBytes'.

"В документе XML есть ошибка (1, 165)."

Сообщение в том виде, в каком оно помещено в очередь

'<?xml version="1.0" encoding="utf-8"?>
  <SccmAction>
    <MachineName>' + @machine_name + '</MachineName>
    <CollectionName>' + @collection_name + '</CollectionName>
    <Action>' + @action + '</Action>
    <EntryDateTime>' + CAST(getdate() AS VARCHAR(100)) + '</EntryDateTime>
  </SccmAction>'

Возможный объект, который я хочу десериализовать до

public class SccmAction : IDelivery
{
    public string MachineName { get; set; }
    public string CollectionName { get; set; }
    public string Action { get; set; }
    public DateTime EntryDateTime { get; set; }

    public void Deliver()
    {
        throw new NotImplementedException();
    }
}

public interface IDelivery
{
    void Deliver();
}

Ответы [ 2 ]

1 голос
/ 17 июля 2009

D on не приводит приведение VARBINARY (MAX) message_body к XML в самой инструкции RECEIVE . Это плохая практика, потому что он может заставить Service Broker поверить, что RECEIVE никогда не происходил, если во время CAST возникает исключительная ситуация проверки XML. Подробнее смотрите по ссылке.

Я рекомендую оставить столбец как VARBINARY (MAX) (т. Е. RECEIVE ..., message_body FROM ...) в списке проекций и в вашем клиенте C # использовать столбец как SqlBytes, а не как байт , Затем вы можете использовать SqlBytes.Stream для создания XmlReader поверх этого потока. Или вы можете читать прямо в XmlDocument.

Не используйте строго типизированные DataTables с RECEIVE, это не соответствует семантике таблиц, ожидаемой сборщиками Visual Studio, и вы причините себе ненужную боль. Используйте SqlDataReader.

Обновление

Не собирайте отправленный XML вручную из строковых патчей. Используйте сам SQL Server для построения XML с помощью предложения FOR XML (обычно с использованием PATH) и присвойте результат переменной XML, которую вы ОТПРАВЛЯЕТЕ:

declare @machine_name sysname
   , @collection_name sysname
   , @action sysname;
select @machine_name = 'Ice Cream'
    , @collection_name = 'Baseball Cards'
    , @action = 'open';

declare @x xml;

select @x = (
   select @machine_name as MachineName
       , @collection_name as CollectionName
       , @action as Action
       , getdate() as EntryDateTime
   for xml path('SccmAction'), type);

send on conversation @h message type [MyMessageType] (@x);

Вам не нужно беспокоиться о форматах дат XML, кодировке UTF или о чем-то в этом роде.

0 голосов
/ 18 июля 2009

Я застрял с байтом [], так как SqlBytes, похоже, не работает.

Наконец, проблема с десериализацией XML для объекта была связана со значением в EntryDateTime, не имеющим формат ISO 8601. Выполнив конвертацию, я смог получить дату в соответствующем формате.

select CONVERT(varchar(30), GETDATE(), 126)
...