在C#app中,我使用以下语句从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();
}
答案 0 :(得分:1)
d on't cast the VARBINARY(MAX) message_body to XML in the RECEIVE statement itself。这是一个不好的做法,因为它可以诱使Service Broker相信如果在CAST期间发生XML验证异常,RECEIVE从未发生过。有关详细信息,请参阅链接。
我建议您将列保留为投影列表中的VARBINARY(MAX)(即RECEIVE ...,message_body FROM ...),并在C#客户端中将列作为SqlBytes使用,而不是作为byte [] 。然后,您可以利用SqlBytes.Stream在此流的顶部构建XmlReader。或者您可以直接阅读XmlDocument。
不要将强类型DataTable与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编码或其他任何内容。
答案 1 :(得分:0)
我坚持使用byte [],因为SqlBytes似乎不起作用。
最后,将XML反序列化为对象的问题与EntryDateTime中的值不是ISO 8601格式有关。通过转换,我能够以适当的格式获取日期。
select CONVERT(varchar(30), GETDATE(), 126)