NServiceBus Xml序列化问题与具有IEnumerable <t>属性的消息</t>

时间:2010-10-05 22:55:02

标签: xml-serialization nservicebus

我正在尝试发送带有IEnumerable属性的消息,我是否认为NServiceBus Xml序列化程序不支持此功能? 如果我切换到使用数组而不是IEnumerable它将工作,如果我使用二进制序列化器它也可以工作

我的消息看起来像这样

[Serializable]
public class Parent : IMessage
{
  public string Identifier { get; private set; }
  public IEnumerable<Child> Children { get; private set; }

  public Parent(string identifier, IEnumerable<Child> children)
  {
    this.Identifier = identifier;
    this.Children = children;
  }
}

[Serializable]
public class Child
{
  public string Identifier { get; private set; }
}  

如果默认的Xml序列化程序无法满足此要求,有没有办法配置替代的Xml序列化程序,如BCL的DataContractSerializer?

提前致谢

专利

2 个答案:

答案 0 :(得分:6)

首先,请注意NServiceBus中的XML序列化与.NET XML序列化不同。 .NET变体的全部内容是能够使用属性定制结果XML以生成特定的XML模式,这可能与其他语言的互操作性。 NServiceBus XML序列化程序是一个极小的功能子集,专门用于尽可能高效地将预定义的消息模式传输到XML或从XML传输。

虽然NServiceBus序列化的结果是可读的(在检查错误队列时非常好)但它不支持所有类型或所有格式化选项。它做它做的事情并且做得很好。

那就是说,IEnumerable的问题在于它可能有很多东西。实际上,它可能只是一个数组,但它可以很容易地成为一个复杂的Linq-to-SQL表达式,它将调用数据库查询。为了序列化IEnumerable,您必须将其表示为集合(列表或数组),因此您必须枚举项目。你什么时候会这样做?交易可能带来哪些问题?这就是性能敏感的NServiceBus XML序列化程序不会打扰的原因。

NServiceBus消息只是传递消息数据的合同。我建议只使用一个数组。将IEnumerable转换为数组(使用ToArray()扩展方法)并返回(使用AsEnumerable()扩展方法)很容易,那么为什么将它作为IEnumerable进行转换很重要?

要完全回答你的问题,应该可以通过编写自己的实现IMessageSerializer的类并配置依赖注入框架来使用它来交换序列化程序,但我自己没有尝试过。这将是一项艰巨的任务,因为每个端点都必须使用相同的串行器,并且您还必须进行修改才能使用分发器,TimeoutManager,Gateway等。

编辑:注意到此问题已在http://tech.groups.yahoo.com/group/nservicebus/message/8838

的NSB小组上交叉发布

答案 1 :(得分:3)

  

有没有办法配置替代Xml序列化程序,如BCL的DataContractSerializer?

是的,这当然是可能的。我们使用DataContractSerializer来提供某些服务。为了实现这一点,您需要实现IMessageSerialzer接口来完成工作,然后在NServiceBus.Configure方法链中使用NServiceBus注册该序列化器。

这是消息序列化程序的代码。这很简单。

public class WcfMessageSerializer : IMessageSerializer
{
    private readonly IList<Type> knownTypes = new List<Type>();

    public IList<Type> MessageTypes
    {
        get { return knownTypes; }
        set
        {
            knownTypes.Clear();
            foreach (var type in value)
            {
                if (!type.IsInterface && typeof(IMessage).IsAssignableFrom(type)
                    && !knownTypes.Contains(type))
                {
                    knownTypes.Add(type);
                }
            }
        }
    }

    public void Serialize(IMessage[] messages, Stream stream)
    {
        var xws = new XmlWriterSettings
        {
            ConformanceLevel = ConformanceLevel.Fragment
        };
        using (var xmlWriter = XmlWriter.Create(stream, xws))
        {
            var dcs = new DataContractSerializer(typeof(IMessage), knownTypes);
            foreach (var message in messages)
            {
                dcs.WriteObject(xmlWriter, message);
            }
        }
    }

    public IMessage[] Deserialize(Stream stream)
    {
        var xrs = new XmlReaderSettings
        {
            ConformanceLevel = ConformanceLevel.Fragment
        };
        using (var xmlReader = XmlReader.Create(stream, xrs))
        {
            var dcs = new DataContractSerializer(typeof(IMessage), knownTypes);
            var messages = new List<IMessage>();
            while (false == xmlReader.EOF)
            {
                var message = (IMessage)dcs.ReadObject(xmlReader);
                messages.Add(message);
            }
            return messages.ToArray();
        }
    }
}

为了将其插入,您可以使用扩展方法,如下所示:

public static class ConfigureWcfSerializer
{
    public static Configure WcfSerializer(this Configure config)
    {
        var messageTypes = Configure.TypesToScan
            .Where(t => typeof(IMessage).IsAssignableFrom(t))
            .ToList();

        config.Configurer
            .ConfigureComponent<WcfMessageSerializer>(ComponentCallModelEnum.Singleton)
            .ConfigureProperty(ms => ms.MessageTypes, messageTypes);

        return config;
    }
}

当你配置NServiceBus时会调用它:

NServiceBus.Configure
    // Other configuration...
    .WcfSerializer()
    // Other configuration...
    .CreateBus()
    .Start();