Select 语句获取字节 [] 和字符串,但不获取 XML

Posted

技术标签:

【中文标题】Select 语句获取字节 [] 和字符串,但不获取 XML【英文标题】:Select Statement Gets byte[] and string but not XML 【发布时间】:2010-11-11 19:23:03 【问题描述】:

在 C# 应用程序中,我使用以下语句从 SQL 服务代理队列中提取消息。

当尝试将 message_body 转换为 SqlBytes 和其他类型时,会引发异常。在运行时 message_body 似乎总是以 byte[] 类型开始。

将 message_body 保留为 byte[] 有效,但在尝试反序列化以键入 SccmAction 时,我收到一个异常,抱怨我的 XML 文档中存在错误。

我的最终目标是以任何形式将 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();

【问题讨论】:

你能发布队列的定义吗?另外,异常到底发生在哪里?请发布完整的异常(ex.ToString() 的输出)。 @JohnSaunders 队列的定义是什么意思? [dbo].[MY_QUEUE_SubmitQueue]的定义 现在我很困惑。如果你知道 message_body 是 byte[],那为什么不把它读成 byte[] 呢? @JohnSaunders 添加了您请求的队列定义。我试图按照 Remus 的建议将其解读为 SqlBytes。 【参考方案1】:

Don't cast the VARBINARY(MAX) message_body to XML in the RECEIVE statement itself。这是一个不好的做法,因为如果在 CAST 期间发生 XML 验证异常,它可以欺骗 Service Broker 相信 RECEIVE 从未发生过。详情见链接。

我建议您在投影列表中将该列保留为 VARBINARY(MAX)(即 RECEIVE ...,message_body FROM ...),并在您的 C# 客户端中将该列作为 SqlBytes 使用,而不是作为 byte[] .然后,您可以利用SqlBytes.Stream 在此流之上构造一个 XmlReader。或者您可以直接读入 XmlDocument。

不要使用带有 RECEIVE 的强类型数据表,它与 Visual Studio 构建器所期望的表语义不匹配,并且会给自己带来不必要的痛苦。使用 SqlDataReader。

更新

不要从字符串补丁手动构建发送的 XML。使用 SQL Server 本身通过 FOR XML 子句(通常使用 PATH)构建 XML,并将结果分配给您发送的 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 编码或任何类似的东西。

【讨论】:

转换仍有问题,请参阅上面已编辑的问题。 顺便说一句,如果不先检查 message_type_name,就无法像那样反序列化消息正文。您总是会收到一条错误消息,并且不会是您在反序列化中期望的 XML 模式...【参考方案2】:

我坚持使用 byte[],因为 SqlBytes 似乎不起作用。

最后,将 XML 反序列化为对象的问题与 EntryDateTime 中的值不是 ISO 8601 格式有关。通过进行转换,我能够以适当的格式获取日期。

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

【讨论】:

以上是关于Select 语句获取字节 [] 和字符串,但不获取 XML的主要内容,如果未能解决你的问题,请参考以下文章

如何在 php 中获取当前浏览器 URL? [复制]

MySQL常用函数

特性select语句中使用字符串链接获取字段值失败

特性select语句中使用字符串链接获取字段值失败

Oracle数据库查询中返回指定的行数例如31到40,主键ID,但不连续,请问这样的语句如何写?

SQL Select 语句获取字符串中包含一个或多个值的所有记录