【问题标题】:NServiceBus messages with properties of base type具有基本类型属性的 NServiceBus 消息
【发布时间】:2015-10-13 12:08:33
【问题描述】:

我们开发了一个 WCF Web 服务,其合同类似于以下内容:

[ServiceContract(Namespace = "XXX")]
public interface ISynchronization
{
    [OperationContract(IsOneWay = true)]
    void ProcessEntities(List<BaseEntity> entities);
}

通过在配置文件的datacontractserializer 下使用BaseEntityknowntypes 配置,它工作得很好并且很容易扩展。

现在需要使用NServiceBus 的可靠消息传递,我需要提出适当的消息格式,能够以通用方式传输实体。

我是NServiceBus 的新手,但据我了解,默认的 NSB 序列化程序不会关心多态性,我被困在每个实体的命令场景中。

上面提到的不是任何类型的轮子改造,必须有一个优雅的解决方案。

使用 DataContractSerializer 代替 NSB 的原生有哪些可能的缺点?


EDIT(继承测试作为对 Ramon 的回答)

我使用了NSB test projects 之一并更改了 CancelOrder,如下所示:

public class CancelOrder : IMessage
{
    public int OrderId { get; set; }
    public List<EntityBase> Entities { get; set; }
}

[KnownType(typeof(Employee))]
[KnownType(typeof(Organization))] //Serialization fails otherwise
public class EntityBase
{
    public virtual string Name { get; set; }
}

public class Employee : EntityBase {
    public override string Name {get{ return "Employee"; }}
    public string EmployeeProperty {get{ return "Employee Property Value"; }}
}

public class Organization : EntityBase
{
    public override string Name {get{ return "Organization"; }}
    public string OrganizationProperty {get { return "Organization Property Value"; }}
}

EntityBase(不是抽象的 - 这也会阻止序列化)具有单个虚拟属性 Name 以便于测试。

测试客户端正在发送一个CancelOrder,其中的实体列表填充了EmployeeOrganization

 var message = new CancelOrder
               {
                 OrderId = orderId++,
                 Entities = new List<EntityBase>
                 {
                   new Employee(), 
                   new Organization()
                 }
               };

 var returnCode = client.Process(message);

一旦传送到服务器,它们就不再是 EntityBase 类型 (message.Entities[0] as Employee == null)

【问题讨论】:

  • 不完全清楚为什么您想要拥有如此灵活的消息。您的界面表明您正在消息传递之上构建自己的“复制”。为什么没有每个实体的消息?为什么要将它们批处理在一条消息中?单个消息具有大小限制。
  • 我正在处理相同类型实体的平面列表(列表大小将根据性能测试的结果来定义)。明天,如果我需要添加一个额外的实体,我将只为BaseEntity 注册相应的knownType,DCS 会处理这个问题。其余部分通过 IoC 容器完成,该容器初始化存储库,然后映射到实体类型。您是否会说每个实体的消息是更好的选择(答案中SyncronizeCommand 中的单个 BaseEntity 属性),为什么?
  • 如果所有新类型的处理完全相同,那么这可以工作,但如果处理不同,那么最好将它们放在不同的消息类型中。此外,我不会在消息中进行批处理,除非它是因为事务边界(所有项目都必须成功,否则不会)。如果这是一种数据复制,那么这是一种反模式。不要在消息传递之上构建复制。为该选项或其他类似的低级选项使用 SQL 服务复制。
  • 这是一个集成。为一个系统提供来自另一个系统的数据。可以按需或按计划进行。
  • 这是与外部系统的集成吗?是不是可以使用复合 UI 的概念来仅将数据集成到 UI 中,而不将该数据的副本保存在另一个系统的数据库中?

标签: .net wcf nservicebus


【解决方案1】:

您的界面可以翻译成以下消息:

public class SynchronizeCommand : IMessage
{
    public int OrderId { get; set; }
    public List<EntityBase> Entities { get; set; }
}

[KnownType(typeof(Employee))] // Required for WCF, not the Json serializer
[KnownType(typeof(Organization))]
public class EntityBase
{
    public virtual string Name { get; set; }
}

public class Employee : EntityBase {
    public override string Name {get{ return "Employee"; }}
    public string EmployeeProperty {get{ return "Employee Property Value"; }}
}

public class Organization : EntityBase
{
    public override string Name {get{ return "Organization"; }}
    public string OrganizationProperty {get { return "Organization Property Value"; }}
}

NServiceBus XML 序列化程序无法序列化,但 Json 序列化程序可以。

你提到的多态性限制是关于消息类型而不是它的属性。

如果您想要触发的处理程序类型中的多态行为,那么您不能对类使用多重继承。只允许接口,因为它们是唯一允许多重继承的类型。

数据合约序列化器的缺点

  • 无法序列化接口,需要一个类
  • 您需要在所有端点上使用 DataContractSerializer
  • NServiceBus 序列化程序在保持消息协定简单方面不太灵活
  • XML 可读性较差

数据合约序列化程序专家

  • 更适合集成目的
  • 支持复杂数据,例如带有循环引用的图表

【讨论】:

  • 感谢您的回答。请检查我在原始问题中的Edit 部分
  • 很抱歉,我不小心用 Json 序列化程序而不是 xml 序列化程序进行了测试。
  • Json 序列化完成了它的工作。您能否更新您的答案(还包括 BaseEntity 的 KnownType 属性),以便它是其他任何有相同问题的人的完整解决方案,我会接受答案。非常感谢!
  • Json 不需要KnownType。
  • 不指定KnownTypes对我不起作用
猜你喜欢
  • 2021-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-15
  • 2013-10-10
  • 1970-01-01
  • 1970-01-01
  • 2011-02-19
相关资源
最近更新 更多