开发者

MSMQ thru WCF is swallowing badly formatted messages, no errors, no warnings

I have a service that responds to messages on an MSMQ, using WCF. When the message is well formatted, it works as normal, and we're happy. When the message is intended for a different service, and the action is different, the message stays on the queue, we get log entries, we fix, and we're happy.

But when the message has the right action, but contains XML elements that cannot be deserialized by the server, the message disappears from the queue, we don't get log entries and we have next to no idea what's happened. we're not happy.

I've tried adding an IErrorHandler. I've tried adding a Faulted event handler. The only thing I can do is use the diagnostic logging built into WCF. Is there any way I can respond to an error like this in code, with my own logger or error handler?

My queues happen to be transactional, but it doesn't seem to any difference to this problem. This happens on MSMQ version 3.0, on Windows Server 2003.

This source code exhibits the problem:

class Program : IErrorHandler {
    private static Uri QueueUri = new Uri("net.msmq://localhost/private/server_queue");

    static void Main(string[] args) {
        Program program = new Program();

        if (args.Length > 0) {
            if (args[0] == "server") {
                program.Server();
            } else if (args[0] == "bad") {
                program.BadClient();
            }
        } else {
            program.Client();
        }
    }

    public void BadClient() {
        using (var client = new BadServiceClient(QueueUri)) {
            client.Do(new [] {new BadStuff { BadMessage = "hi" }});
        }
    }

    public void Client() {
        using (var client = new ServiceClient(QueueUri)) {
            client.Do(new [] {new Stuff {Message = "hi"}});
        }
    }

    public void Server() {
        var serviceHost = new ServiceHost(typeof(Service));
        serviceHost.AddServiceEndpoint(typeof (开发者_运维技巧IService), new NetMsmqBinding(), QueueUri);
        serviceHost.Open();

        serviceHost.Faulted += serviceHost_Faulted;
        serviceHost.UnknownMessageReceived += new EventHandler<UnknownMessageReceivedEventArgs>(serviceHost_UnknownMessageReceived);
        foreach (ChannelDispatcher dispatcher in serviceHost.ChannelDispatchers) {
            dispatcher.ErrorHandlers.Add(this);
        }

        Console.WriteLine("press the any key");
        Console.ReadKey(true);
    }

    void serviceHost_UnknownMessageReceived(object sender, UnknownMessageReceivedEventArgs e) {
        Console.WriteLine("unknown message: {0}", e);
    }

    private static void serviceHost_Faulted(object sender, EventArgs e) {
        Console.WriteLine("fault: {0}", e);
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault) {
        Console.WriteLine("handling error: {0}", error);
    }

    public bool HandleError(Exception error) {
        Console.WriteLine("handling error: {0}", error);
        return true;
    }
}

[ServiceContract]
public interface IService {
    [OperationContract(IsOneWay = true, Action = "do")]
    void Do(IEnumerable<Stuff> stuffs);
}

[ServiceContract]
public interface IBadService {
    [OperationContract(IsOneWay = true, Action = "do")]
    void Do(IEnumerable<BadStuff> stuffs);
}

class ServiceClient : ClientBase<IService>, IService {
    public ServiceClient(Uri uri) : base(new NetMsmqBinding(), new EndpointAddress(uri)) {}

    public void Do(IEnumerable<Stuff> stuffs) {
        Channel.Do(stuffs);
    }
}

class BadServiceClient : ClientBase<IBadService>, IBadService {
    public BadServiceClient(Uri uri) : base(new NetMsmqBinding(), new EndpointAddress(uri)) {}

    public void Do(IEnumerable<BadStuff> stuffs) {
        Channel.Do(stuffs);
    }
}

[ServiceBehavior(TransactionTimeout = "00:5:00", InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
class Service : IService {
    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public void Do(IEnumerable<Stuff> stuffs) {
        foreach (var stuff in stuffs) {
            Console.WriteLine("service doing stuff, message: " + stuff.Message);
        }
    }
}

[DataContract]
public class Stuff {
    [DataMember]
    public string Message;
}

[DataContract]
public class BadStuff : Stuff {
    [DataMember]
    public string BadMessage;
}


This is very late, but adding an answer for others who may have a similar issue like me. Note the use of MSMQ is not related to not being able to extract the malformed message. As Tim has mentioned using a IDispatchMessageInspector implementation will not give you the message if it fails to deserialise the XML because it's malformed (e.g contains an & char).

The only thing I could find is to turn on regular WCF message tracing where logMalformedMessages is set to true under the messageLogging element as per this documentation (hopefully MS don't move the link):

Configuring Message Logging

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜