【问题标题】:Sending a list of different objects via SignalR通过 SignalR 发送不同对象的列表
【发布时间】:2020-02-10 13:14:21
【问题描述】:

通过 SignalR 向客户端发送不同对象列表的最佳方式是什么?

当我使用具有相同基类的对象列表时,客户端只接收基类的属性:

class Update { }
class UpdateA { public string PropertyA {get; set;}}
class UpdateB { public string PropertyB {get; set;}}
...
IEnumerable<Update> updates = GetUpdates();
await Clients.Caller.SendAsync("update", updates);

当我使用字符串形式的预序列化数据时,serialzier 将 json 数据转义为字符串。

IEnumerable<string> updates = GetUpdates();
await Clients.Caller.SendAsync("update", updates);
// data looks like: [ "{...}", "{...}" ]

我必须编写自己的序列化器吗? SignalR 在 dotnet core 中是如何工作的?

【问题讨论】:

  • 为什么不直接使用一个复杂的类,包含所有的对象,最后发送给客户端呢?

标签: c# .net-core signalr-hub asp.net-core-signalr


【解决方案1】:

通过 SignalR 向客户端发送具有相同基础的不同对象的列表

您可以尝试通过创建和使用自定义转换器来实现要求,如下所示。

在自定义转换器MyConverterWithTypeDiscriminator

public override void Write(Utf8JsonWriter writer, Update value, JsonSerializerOptions options)
{
    writer.WriteStartObject();

    if (value is UpdateA updateA)
    {
        writer.WriteNumber("TypeDiscriminator", (int)TypeDiscriminator.UpdateA);
        writer.WriteString("PropertyA", updateA.PropertyA);
    }
    else if (value is UpdateB updateB)
    {
        writer.WriteNumber("TypeDiscriminator", (int)TypeDiscriminator.UpdateB);
        writer.WriteString("PropertyB", updateB.PropertyB);
    }

    writer.WriteString("UpdatedAt", value.UpdatedAt);

    writer.WriteEndObject();
}

基类和派生类

public class Update
{
    public DateTime UpdatedAt { get; set; }
}

public class UpdateA : Update
{
    public string PropertyA { get; set; }
}

public class UpdateB : Update
{
    public string PropertyB { get; set; }
}

在 Startup.cs 中

services.AddSignalR().AddJsonProtocol(options => options.PayloadSerializerOptions.Converters.Add(new MyConverterWithTypeDiscriminator()));

在 Hub 方法

var updates = new List<Update>
{
    new UpdateA
    {
        UpdatedAt=DateTime.Now.AddDays(-1),
        PropertyA = "A"
    },
    new UpdateB
    {
        UpdatedAt=DateTime.Now.AddDays(-1),
        PropertyB = "B"
    }
};

await Clients.All.SendAsync("ReceiveUpdate", updates);

测试结果

注意:要实现您的自定义转换器,请查看此链接:https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to#support-polymorphic-deserialization

【讨论】:

    【解决方案2】:

    我知道这是一个老问题,但这里有一个不需要编写自己的 CustomConverter 而是依赖于 NewtonSoft 解决方案的其他解决方案:

    1. 首先你需要安装这个包:Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson

    2. 在服务器端,您需要在Startup.cs 类中添加以下调用:

      services.AddSignalR()
          .AddNewtonsoftJsonProtocol(opts => 
              opts.PayloadSerializerSettings.TypeNameHandling = TypeNameHandling.Auto);
      
    3. 如果您的客户端是 C# 客户端,则在建立连接时必须添加相同的配置:

      new HubConnectionBuilder()
          .WithUrl("/yourhub")
          .AddNewtonsoftJsonProtocol(opts => 
              opts.PayloadSerializerSettings.TypeNameHandling = TypeNameHandling.Auto)
          .Build();
      

    如果您的客户端不是 C# 客户端,我想您将不得不自己管理反序列化。 NewtonSoft.Json 只是在生成的 JSON 中添加一个 $type 属性,其中包含完全限定的类型名称(例如:Your.Namespace.ClassName),以便您可以使用它来选择要反序列化的相应类型。

    来源:MSDN

    【讨论】:

      猜你喜欢
      • 2018-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-19
      相关资源
      最近更新 更多