9

我想知道是否可以将 SignalR 消息直接添加到 SignalR SQL 背板(来自 SQL),这样我就不必使用 SignalR 客户端来执行此操作。

我的情况是我有一个为 SQL Service Broker 队列激活的存储过程,当它触发时,我想向 SignalR 客户端发布一条消息。目前,我必须在单独的过程中从 SQL Service Broker 接收消息,然后立即使用 SignalR 集线器重新发送消息。

我希望我激活的存储过程基本上将消息直接移动到 SignalR SQL 背板上。

4

2 回答 2

1

是和不是。我在我的本地主机上设置了一个小实验以确定是否可能 - 如果格式正确,它是。

现在,[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 函数。这个页面更多地讨论了如何做到这一点。

于 2016-11-12T05:36:16.730 回答
0

我受到这篇文章的启发,并在 SQL 存储过程中编写了一个版本。工作真的很流畅,并不难。

我以前没有用 varbinary 做过很多工作——但是 SQL Server 使它很容易使用,你可以把这些部分加在一起。上面 James Haug 给出的格式是准确的。大多数字符串只是“长度为字节,然后是字符串内容”(字符串内容只是转换(varbinary,string))。异常字符串是有效负载,而是“长度为 int32,然后是字符串内容”。数字以“最低有效字节优先”写出。我不确定您是否可以在本机进行这样的转换 - 我发现自己将其编写为递归函数很容易(类似于 numToBinary(val,bytesRemaining)... 返回 varbinary)。

如果您采用这条路线,我仍然会先编写一个解析器(使用 .NET 或其他非 SQL 语言)并在 SignalR 本身生成的一些数据包上对其进行测试。这为您提供了一个更好的地方来解决 SQL 中的问题 - 并了解有效负载包的正确格式以及其他内容。

于 2021-03-21T20:54:54.340 回答