我想知道是否可以将 SignalR 消息直接添加到 SignalR SQL 背板(来自 SQL),这样我就不必使用 SignalR 客户端来执行此操作。
我的情况是我有一个为 SQL Service Broker 队列激活的存储过程,当它触发时,我想向 SignalR 客户端发布一条消息。目前,我必须在单独的过程中从 SQL Service Broker 接收消息,然后立即使用 SignalR 集线器重新发送消息。
我希望我激活的存储过程基本上将消息直接移动到 SignalR SQL 背板上。
我想知道是否可以将 SignalR 消息直接添加到 SignalR SQL 背板(来自 SQL),这样我就不必使用 SignalR 客户端来执行此操作。
我的情况是我有一个为 SQL Service Broker 队列激活的存储过程,当它触发时,我想向 SignalR 客户端发布一条消息。目前,我必须在单独的过程中从 SQL Service Broker 接收消息,然后立即使用 SignalR 集线器重新发送消息。
我希望我激活的存储过程基本上将消息直接移动到 SignalR SQL 背板上。
是和不是。我在我的本地主机上设置了一个小实验以确定是否可能 - 如果格式正确,它是。
现在,[SignalR]谈谈架构。它生成三个表:
[SignalR].[Messages_0]
--this holds a list of all messages with the columns of
--[PayloadId], [Payload], and [InsertedOn]
[SignalR].[Messages_0_Id]
--this holds one record of one field - the last Id value in the [Messages_0] table
[SignalR].[Scehma]
--No idea what this is for; it's a 1 column (SchemaVersion) 1 record (value of 1) table
是的,所以,我复制了最后一列,除了我增加了PayloadId(对于新记录并输入[Messages_0_Id]并GETDATE()作为 的值InsertedOn。添加记录后,一条新消息立即进入连接的客户端。请注意,这PayloadId不是身份列,因此您必须手动递增它,并且必须将该递增的值复制到 中的唯一记录中[Messages_0_Id],否则您的信号器客户端将由于 Signalr SQL 错误而无法连接。
现在,诀窍是正确填充 [Payload] 列。快速查看该表表明它可能是二进制序列化的。我不是 SQL 专家,但我很确定进行二进制序列化是很困难的。如果我是对的,这是二进制序列化的源代码,位于内部Microsoft.AspNet.SignalR.Messaging.ScaleoutMessage:
public byte[] ToBytes()
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryWriter binaryWriter = new BinaryWriter((Stream) memoryStream);
binaryWriter.Write(this.Messages.Count);
for (int index = 0; index < this.Messages.Count; ++index)
this.Messages[index].WriteTo((Stream) memoryStream);
binaryWriter.Write(this.ServerCreationTime.Ticks);
return memoryStream.ToArray();
}
}
与WriteTo:
public void WriteTo(Stream stream)
{
BinaryWriter binaryWriter = new BinaryWriter(stream);
string source = this.Source;
binaryWriter.Write(source);
string key = this.Key;
binaryWriter.Write(key);
int count1 = this.Value.Count;
binaryWriter.Write(count1);
ArraySegment<byte> arraySegment = this.Value;
byte[] array = arraySegment.Array;
arraySegment = this.Value;
int offset = arraySegment.Offset;
arraySegment = this.Value;
int count2 = arraySegment.Count;
binaryWriter.Write(array, offset, count2);
string str1 = this.CommandId ?? string.Empty;
binaryWriter.Write(str1);
int num1 = this.WaitForAck ? 1 : 0;
binaryWriter.Write(num1 != 0);
int num2 = this.IsAck ? 1 : 0;
binaryWriter.Write(num2 != 0);
string str2 = this.Filter ?? string.Empty;
binaryWriter.Write(str2);
}
因此,用纯 SQL 在存储过程中重新实现它几乎是不可能的。如果您需要在 SQL Server 上执行此操作,我建议使用SQL CLR 函数。不过要提一件事 - 使用类库很容易,但如果你想长期减少麻烦,我建议在 Visual Studio 中创建一个 SQL Server 项目。与手动将最新的类库重新复制到 SQL Server 相比,这将允许您更轻松地自动部署 CLR 函数。这个页面更多地讨论了如何做到这一点。
我受到这篇文章的启发,并在 SQL 存储过程中编写了一个版本。工作真的很流畅,并不难。
我以前没有用 varbinary 做过很多工作——但是 SQL Server 使它很容易使用,你可以把这些部分加在一起。上面 James Haug 给出的格式是准确的。大多数字符串只是“长度为字节,然后是字符串内容”(字符串内容只是转换(varbinary,string))。异常字符串是有效负载,而是“长度为 int32,然后是字符串内容”。数字以“最低有效字节优先”写出。我不确定您是否可以在本机进行这样的转换 - 我发现自己将其编写为递归函数很容易(类似于 numToBinary(val,bytesRemaining)... 返回 varbinary)。
如果您采用这条路线,我仍然会先编写一个解析器(使用 .NET 或其他非 SQL 语言)并在 SignalR 本身生成的一些数据包上对其进行测试。这为您提供了一个更好的地方来解决 SQL 中的问题 - 并了解有效负载包的正确格式以及其他内容。