Erreur dans la file d'attente des messages: impossible de trouver un formatteur capable de lire le message
j'écris des messages dans une file D'attente en C# comme suit:
queue.Send(new Message("message"));
je suis en train de lire les messages de la façon suivante:
Messages messages = queue.GetAllMessages();
foreach(Message m in messages)
{
String message = m.Body;
//do something with string
}
Cependant j'obtiens un message d'erreur qui dit: "Impossible de trouver un formateur capable de lire ce message."
Ce que je fais mal?
9 réponses
j'ai résolu le problème en ajoutant un formatteur à chaque message. Ajouter un formatteur à la file d'attente n'a pas fonctionné.
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
}
Ou vous pouvez utiliser
message.Formatter =
new System.Messaging.XmlMessageFormatter(new Type[1] { typeof(string) });
Vous pouvez essayer de lire le corps du message au lieu du corps, comme ceci:
StreamReader sr = new StreamReader(m.BodyStream);
string messageBody = "";
while (sr.Peek() >= 0)
{
messageBody += sr.ReadLine();
}
il semble que la sérialisation ne soit faite qu'en accédant à la propriété Body de la classe de Message. Aussi longtemps que vous accédez à la propriété Body après avoir défini sur le message le bon Formatteur, il fonctionne très bien.
si vous préférez ne pas créer un Formatteur pour chaque message, vous pouvez définir le Formatteur sur la file d'attente et pour chaque message (avant d'accéder à la propriété Body) définir la propriété Formatter à partir du Formatteur de la file d'attente.
_queue.Send(new Message() { Formatter = _queue.Formatter, Body = myData } );
var msg = _qeueu.Receive();
msg.Formatter = _queue.Formatter;
var myObject = (MyClass) msg.Body;
Message recoverableMessage = new Message();
recoverableMessage.Body = "Sample Recoverable Message";
recoverableMessage.Formatter = new XmlMessageFormatter(new String[] {"System.String,mscorlib" });
MessageQueue myQueue = new MessageQueue(@".\private$\teste");
la file d'attente doit aussi être définie Formatter.
myQueue.Formatter = new XmlMessageFormatter(new String[] { "System.String,mscorlib" });
tout le monde ici a fait un travail fantastique à fournir des solutions, et ayant juste fini de combattre ce problème moi-même, je voulais jeter mon propre 2c et montrer la solution que j'ai trouvé qui fonctionne très bien.
tout d'abord, lorsque la file d'attente est créée, Je m'assure d'ouvrir les permissions comme cela (je ne me soucie pas de la sécurité de la file d'attente dans le contexte de notre application... c'est une décision délibérée):
queue.SetPermissions("Everyone", MessageQueueAccessRights.FullControl, AccessControlEntryType.Set);
sans cette ligne, je recevrais toutes sortes de erreurs inaccessibles et ne pouvait même pas parcourir la file d'attente à partir de l'écran de gestion de l'ordinateur. D'ailleurs si cela vous arrive et que vous vous demandez comment tuer la file d'attente que vous n'avez pas accès à:
- Arrêter le service "Message Queueing"
- Goto "C:\Windows\System32\msmq\storage\lqs"
- ouvrez chaque fichier dans le bloc-notes et recherchez votre nom de file d'attente (il sera très probablement le fichier qui a été le plus récemment modifié)
- Supprimer ce fichier et redémarrer le service de messagerie
créez une classe de base pour vos messages de file d'attente et marquez-la [sérialisable]. Sur application load cache une liste de tous les types de messages en utilisant quelque chose comme ceci:
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);
Maintenant vous êtes prêt à commencer à recevoir des messages. Mon premier instinct a été de rechercher des messages, mais l'api n'aime pas vraiment travailler de cette façon. Donc je crée un thread de fond et j'appelle la méthode de blocage Receive sur la file qui va de retour une fois qu'un message est disponible. De là, décoder le message est aussi simple que:
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;
et ce devrait être tout ce dont vous avez besoin pour obtenir bien mis en œuvre, typesafe intégration MSMQ!
Cela a fonctionné pour moi de lire une file d'attente privée à partir d'un ordinateur distant:
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();
cela fonctionne très bien:
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();
}
ajout de formatter résolu mon problème:
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);
}
}
Vous pouvez voir le code échantillon et la bibliothèque msmq sur github: https://github.com/beyazc/MsmqInt