Проблема сериализации NServiceBus Xml с сообщениями, имеющими свойство IEnumerable <T> - PullRequest
4 голосов
/ 06 октября 2010

Я пытаюсь отправить сообщение со свойством 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, такой как DataContractSerializer BCL?

Заранее спасибо

1010 * Пат *

Ответы [ 2 ]

6 голосов
/ 06 октября 2010

Во-первых, обратите внимание, что сериализация XML в NServiceBus - это не то же самое, что сериализация .NET XML. Вариант .NET - это возможность адаптировать полученный XML с атрибутами для создания определенных схем XML, потенциально для взаимодействия с другими языками. Сериализатор NServiceBus XML представляет собой чрезвычайно малое подмножество функций, предназначенных для максимально эффективной передачи предопределенных схем сообщений в XML и из XML.

Хотя результат сериализации NServiceBus читабелен (что очень удобно при проверке очередей ошибок), он не поддерживает все типы или все параметры форматирования. Он делает то, что делает, и делает это очень хорошо.

Тем не менее, проблема с IEnumerable заключается в том, что в нем может быть так много вещей. В действительности это может быть просто массив, но это может быть и сложное выражение Linq-to-SQL, которое вызовет запрос к базе данных. Чтобы сериализовать IEnumerable, вам все равно придется представлять его как коллекцию (список или массив), поэтому вы должны перечислять элементы. Когда именно ты это сделаешь? Какие проблемы с транзакциями могут возникнуть? Вот почему не заботит производительность XML-сериализатор NServiceBus.

Сообщение NServiceBus - это просто контракт на передачу данных сообщения. Я бы предложил просто использовать массив. Достаточно просто преобразовать IEnumerable в массив (с помощью метода расширения ToArray ()) и обратно (с помощью метода расширения AsEnumerable ()), так почему важно иметь его как IEnumerable?

Чтобы полностью ответить на ваш вопрос, можно иметь возможность заменить сериализатор, написав собственный класс, который реализует IMessageSerializer, и сконфигурировать инфраструктуру внедрения зависимостей для его использования, но я сам не пробовал этого. Это было бы довольно непросто, поскольку каждая конечная точка должна была бы использовать один и тот же сериализатор, и вам также пришлось бы вносить изменения, чтобы использовать Distributor, TimeoutManager, Gateway и т. Д.

Редактировать: Заметил, что этот вопрос был перекрестно опубликован в группе NSB на http://tech.groups.yahoo.com/group/nservicebus/message/8838

3 голосов
/ 28 января 2013

есть ли способ настроить альтернативный сериализатор Xml, такой как DataContractSerializer BCL?

Да, это, безусловно, возможно.Мы используем DataContractSerializer для некоторых наших услуг.Чтобы это работало, вам нужно реализовать интерфейс IMessageSerialzer, который выполняет эту работу, а затем зарегистрировать этот сериализатор в NServiceBus в цепочке методов NServiceBus.Configure.

Вот код для сериализатора сообщений.Это довольно просто.

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();
...