Ошибка очереди сообщений: не удается найти форматировщик, способный читать сообщение
Я пишу сообщения в очередь сообщений в С# следующим образом:
queue.Send(new Message("message"));
Я пытаюсь прочитать сообщения следующим образом:
Messages messages = queue.GetAllMessages();
foreach(Message m in messages)
{
String message = m.Body;
//do something with string
}
Однако я получаю сообщение об ошибке, в котором говорится: "Невозможно найти форматировщик, способный читать это сообщение".
Что я делаю неправильно?
Ответы
Ответ 1
Я решил проблему, добавив форматтер для каждого сообщения. Добавление форматирования в очередь не сработало.
Messages messages = queue.GetAllMessages();
foreach(Message m in messages)
{
m.Formatter = new XmlMessageFormatter(new String[] { "System.String,mscorlib" });
String message = m.Body;
//do something with string
}
Ответ 2
Или вы можете использовать
message.Formatter =
new System.Messaging.XmlMessageFormatter(new Type[1] { typeof(string) });
Ответ 3
вы можете попробовать прочесть боди-поток сообщения вместо тела, например:
StreamReader sr = new StreamReader(m.BodyStream);
string messageBody = "";
while (sr.Peek() >= 0)
{
messageBody += sr.ReadLine();
}
Ответ 4
Кажется, что сериализация выполняется только при доступе к свойству Body класса Message. До тех пор, пока вы получаете доступ к свойству Body после того, как вы установите на сообщение, правильный Formatter работает нормально.
Если вы предпочитаете не создавать Formatter для каждого сообщения, вы можете установить Formatter в очереди, и для каждого сообщения (перед доступом к свойству Body) установите свойство Formatter из Formatter очереди.
_queue.Send(new Message() { Formatter = _queue.Formatter, Body = myData } );
var msg = _qeueu.Receive();
msg.Formatter = _queue.Formatter;
var myObject = (MyClass) msg.Body;
Ответ 5
Все здесь проделали фантастическую работу по предоставлению решений, и, только что закончив борьбу с этой проблемой, я хотел бросить свой собственный 2c и показать, какое решение я придумал, это работает очень хорошо.
Во-первых, когда очередь создается, я убеждаюсь, что я открываю такие разрешения (я не беспокоюсь о безопасности очередей в контексте нашего приложения... это вычисленное решение):
queue.SetPermissions("Everyone", MessageQueueAccessRights.FullControl, AccessControlEntryType.Set);
Без этой строки я получал бы всевозможные недостижимые ошибки и даже не мог просматривать очередь с экрана управления компьютером. Кстати, если это происходит с вами, и вам интересно, как убить очередь, в которой у вас нет доступа:
- Остановить службу "Очередь сообщений"
- Перейти к "C:\Windows\System32\msmq\storage\lqs"
- Откройте каждый файл в блокноте и найдите имя очереди (скорее всего, это файл, который был недавно изменен)
- Удалите этот файл и перезапустите службу обмена сообщениями
Создайте базовый класс для ваших сообщений в очереди и отметьте его [Serializable].
В кеше загрузки приложений список всех типов сообщений используется примерно так:
var types = typeof(QueueItemBase).Assembly
.GetTypes()
.Where(t => typeof(QueueItemBase).IsAssignableFrom(t) && t.IsAbstract == false)
.ToArray();
...
// Create and cache a message formatter instance
_messageFormatter = new XmlMessageFormatter(types);
Теперь вы готовы начать получать сообщения. Мой первый инстинкт состоял в том, чтобы опросить сообщения, но api не очень нравится работать именно так. Поэтому я создаю фоновый поток и вызываю метод блокировки. Получаю в очереди, которая будет возвращаться после получения сообщения. Оттуда декодирование сообщения так же просто, как:
var message = queue.Receive();
if (message == null)
continue;
// Tell the message about our formatter containing all our message types before we
// try and deserialise
message.Formatter = _messageFormatter;
var item = message.Body as QueueItemBase;
И это должно быть все, что вам нужно для того, чтобы получить красиво реализованную, типичную интеграцию с MSMQ!
Ответ 6
Message recoverableMessage = new Message();
recoverableMessage.Body = "Sample Recoverable Message";
recoverableMessage.Formatter = new XmlMessageFormatter(new String[] {"System.String,mscorlib" });
MessageQueue myQueue = new MessageQueue(@".\private$\teste");
В очереди также должен быть установлен Formatter.
myQueue.Formatter = new XmlMessageFormatter(new String[] { "System.String,mscorlib" });
Ответ 7
это работает очень хорошо:
static readonly XmlMessageFormatter f = new XmlMessageFormatter(new Type[] { typeof(String) });
private void Client()
{
var messageQueue = new MessageQueue(@".\Private$\SomeTestName");
foreach (Message message in messageQueue.GetAllMessages())
{
message.Formatter = f;
Console.WriteLine(message.Body);
}
messageQueue.Purge();
}
Ответ 8
Это помогло мне прочитать приватную очередь с удаленной машины:
MessageQueue queue = new MessageQueue(@"FormatName:Direct=OS:MACHINENAME\private$\MyQueueName", QueueAccessMode.Peek);
Message msg = queue.Peek();
StreamReader sr = new StreamReader(msg.BodyStream);
string messageBody = sr.ReadToEnd();
Ответ 9
Добавление форматирования позволило решить мою проблему:
public void ReceiveAsync<T>(MqReceived<T> mqReceived)
{
try
{
receiveEventHandler = (source, args) =>
{
var queue = (MessageQueue)source;
using (Message msg = queue.EndPeek(args.AsyncResult))
{
XmlMessageFormatter formatter = new XmlMessageFormatter(new Type[] { typeof(T) });
msg.Formatter = formatter;
queue.ReceiveById(msg.Id);
T tMsg = (T)msg.Body;
mqReceived(tMsg);
}
queue.BeginPeek();
};
messageQueu.PeekCompleted += receiveEventHandler;
messageQueu.BeginPeek();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
Вы можете увидеть пример кода и библиотеку msmq на github:
https://github.com/beyazc/MsmqInt