NServiceBus 6升7后SQL传输JSON序列化兼容旧逻辑的替代方案问询
升级NServiceBus SQL传输到v7:无需修改触发器或指定所有消息类型的序列化方案
我前段时间刚处理过完全相同的NServiceBus升级场景,你碰到的这个JsonSerializationException其实是新旧序列化器的行为差异导致的:旧的NServiceBus.JsonSerializer会自动忽略$type里缺失的程序集名称,在已知的消息程序集里查找匹配类型;而NewtonsoftSerializer默认要求$type必须包含完整的程序集限定名,所以才会抛出类型无法解析的异常。
你提到的两种方案都有明显的痛点——改触发器太繁琐,指定所有消息类型又不灵活。这里给你第三种更优雅的解决方案:自定义一个轻量级的ISerializationBinder,自动补全程序集名称,同时不需要手动维护所有消息类型的列表。
实现自定义序列化绑定器
这个绑定器的核心逻辑是:
- 反序列化时,如果
$type没有程序集名称,就在你指定的消息程序集里自动查找匹配的类型 - 序列化时输出不带程序集名称的短类型名,保持和旧序列化器的格式一致,兼容现有触发器和未升级的端点
代码示例如下:
public class ShortTypeNameBinder : ISerializationBinder { private readonly Assembly[] _messageAssemblies; public ShortTypeNameBinder(params Assembly[] messageAssemblies) { _messageAssemblies = messageAssemblies ?? throw new ArgumentNullException(nameof(messageAssemblies)); } public Type BindToType(string assemblyName, string typeName) { // 如果$type已经包含完整程序集名称,直接用默认逻辑解析 if (!string.IsNullOrEmpty(assemblyName)) { return Type.GetType($"{typeName}, {assemblyName}"); } // 遍历指定的消息程序集,查找匹配的类型 foreach (var assembly in _messageAssemblies) { var targetType = assembly.GetType(typeName); if (targetType != null) { return targetType; } } throw new JsonSerializationException($"无法解析类型 '{typeName}',请检查消息程序集配置"); } public void BindToName(Type serializedType, out string assemblyName, out string typeName) { // 序列化时只输出类型的完整名称,不带程序集信息 assemblyName = null; typeName = serializedType.FullName; } }
在端点配置中启用这个绑定器
只需要在配置NewtonsoftSerializer时,把自定义绑定器注入进去,同时指定你的消息所在的程序集(支持多个程序集):
// 加载你的消息程序集,比如MyAssembly.Events var messageAssembly = Assembly.Load("MyAssembly.Events"); // 如果有多个消息程序集,可以添加更多:Assembly.Load("MyAssembly.Commands"), ... endpointConfiguration.UseSerialization<NewtonsoftSerializer>() .Settings(settings => { settings.SerializationBinder = new ShortTypeNameBinder(messageAssembly); });
这个方案的优势
- 无需修改触发器:完全保留原来不带程序集名称的
$type格式,不用批量修改SQL语句 - 无需维护类型列表:只需要指定消息所在的程序集,新增消息时不需要更新配置
- 双向兼容:序列化输出的格式和旧序列化器一致,不会影响未升级的消费端点
- 性能友好:只在指定的程序集里查找类型,不会遍历所有加载的程序集
注意事项
确保你的消息类型的FullName在指定的程序集里是唯一的——如果有两个不同的类型拥有完全相同的FullName(比如不同程序集里的同名同命名空间类型),绑定器会返回第一个找到的类型,这可能导致错误。不过在正常的消息设计中,这种情况应该是要避免的。
内容的提问来源于stack exchange,提问作者Alpha75
相关产品推荐
相关产品推荐

