Fila de Mensagem de erro: não é possível encontrar um formatador capaz de mensagem de leitura
-
19-08-2019 - |
Pergunta
Eu estou escrevendo mensagens para uma fila de mensagens em C # da seguinte forma:
queue.Send(new Message("message"));
Eu estou tentando ler as mensagens da seguinte forma:
Messages messages = queue.GetAllMessages();
foreach(Message m in messages)
{
String message = m.Body;
//do something with string
}
No entanto, eu estou recebendo uma mensagem de erro que diz: "Não consegue encontrar um formatador capaz de ler esta mensagem"
O que estou fazendo de errado?
Solução
Eu resolvi o problema adicionando um formatador para cada mensagem. Adicionando um formatador para a fila não trabalho.
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
}
Outras dicas
Ou você pode usar
message.Formatter =
new System.Messaging.XmlMessageFormatter(new Type[1] { typeof(string) });
você pode tentar ler o BodyStream da mensagem em vez do corpo, como este:
StreamReader sr = new StreamReader(m.BodyStream);
string messageBody = "";
while (sr.Peek() >= 0)
{
messageBody += sr.ReadLine();
}
Message recoverableMessage = new Message();
recoverableMessage.Body = "Sample Recoverable Message";
recoverableMessage.Formatter = new XmlMessageFormatter(new String[] {"System.String,mscorlib" });
MessageQueue myQueue = new MessageQueue(@".\private$\teste");
fila de espera deve ser definido Formatter também.
myQueue.Formatter = new XmlMessageFormatter(new String[] { "System.String,mscorlib" });
Parece que a serialização é feito somente quando estiver acessando a propriedade corpo da classe Message. Contanto que você acessar a propriedade corpo depois de definir sobre a mensagem à direita Formatter ele funciona bem.
Se você não preferir criar uma Formatter para cada mensagem pode definir o formatador na fila e para cada mensagem (antes de acessar a propriedade Body) definir a propriedade Formatter do Formatter da fila.
_queue.Send(new Message() { Formatter = _queue.Formatter, Body = myData } );
var msg = _qeueu.Receive();
msg.Formatter = _queue.Formatter;
var myObject = (MyClass) msg.Body;
Todo mundo aqui tem feito um trabalho fantástico em soluções que fornecem, e tendo acabado de lutar este problema me eu queria jogar meu próprio 2c e mostrar a solução que eu vim com que funciona muito bem.
Em primeiro lugar, quando a fila é criada eu me certificar de que abrir as permissões assim (eu não estou preocupado com a segurança de fila no contexto da nossa aplicação ... esta é uma decisão calculada):
queue.SetPermissions("Everyone", MessageQueueAccessRights.FullControl, AccessControlEntryType.Set);
Sem essa linha que eu iria receber todos os tipos de erros de difícil acesso e não poderia até mesmo navegar na fila da tela de gerenciamento do computador. Aliás, se isso acontecer com você e você está querendo saber como matar a fila que você não tem acesso a apenas:
- Parar o serviço "Message Queuing"
- Goto "C: \ Windows \ System32 \ msmq \ STORAGE \ LQS"
- Abra cada arquivo no bloco de notas e olhar para o seu nome da fila (que provavelmente será o arquivo que foi modificado mais recentemente)
- Excluir o arquivo e reinicie o serviço de mensagens
Criar uma classe base para seus itens Message Queue e marcá-lo [Serializable]. Em cache de carga de aplicativos uma lista de todos os tipos de mensagens usando algo parecido com isto:
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);
Agora você está pronto para começar a receber mensagens. Meu primeiro instinto foi o de pesquisa para mensagens, mas a API não realmente gosto de trabalhar dessa forma. Então eu criar uma discussão de fundo e chamar o método de bloqueio de recepção na fila que irá retornar uma vez que uma mensagem está disponível. De lá decodificar a mensagem é tão simples como:
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;
E isso deve ser tudo que você precisa para começar bem implementado, typesafe integração MSMQ!
Isso funcionou para mim ler uma fila particular de uma máquina remota:
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();
isso funciona muito bem:
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();
}
Adicionando formatador resolvido o meu problema:
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);
}
}
Você pode ver exemplos de código e biblioteca msmq no github: https://github.com/beyazc/MsmqInt