wcfxml-deserializationmessagecontract

WCF: MessageContract handle OnDeserialized Event


How can I handle an OnDeserialized event using MessageContract?

Need to do some data verification and transformation after receiving the message (but before executing methods).

For DataContract it was solved by declaration attribute.

But for MessageContract it doesn't work.

Is there any way to do this?


Solution

  • You are better of using WCF extension points instead of serialization. Specifically IOperationInvoker.

    EDIT

    Example:

    Service and message definitions below. Note the new MyValidationBeforeInvokeBehavior attribute.

    [ServiceContract]
        public interface IService1
        {
    
            [OperationContract]
            string GetData(int value);
    
            [OperationContract]
            [MyValidationBeforeInvokeBehavior]
            AddPatientRecordResponse AddPatientRecord(PatientRecord composite);
        }
    
        [MessageContract(IsWrapped = false, ProtectionLevel = ProtectionLevel.None)]
        public class AddPatientRecordResponse
        {
            [MessageHeader(ProtectionLevel = ProtectionLevel.None)]
            public Guid recordID;
            [MessageHeader(ProtectionLevel = ProtectionLevel.None)]
            public string patientName;
            [MessageBodyMember(ProtectionLevel = ProtectionLevel.None)]
            public string status;
    
        }
    
    
        [MessageContract(IsWrapped = false, ProtectionLevel = ProtectionLevel.None)]
        public class PatientRecord
        {
            [MessageHeader(ProtectionLevel = ProtectionLevel.None)]
            public Guid recordID;
            [MessageHeader(ProtectionLevel = ProtectionLevel.None)]
            public string patientName;
    
            //[MessageHeader(ProtectionLevel = ProtectionLevel.EncryptAndSign)]
            [MessageHeader(ProtectionLevel = ProtectionLevel.None)]
            public string SSN;
            [MessageBodyMember(ProtectionLevel = ProtectionLevel.None)]
            public string comments;
            [MessageBodyMember(ProtectionLevel = ProtectionLevel.None)]
            public string diagnosis;
            [MessageBodyMember(ProtectionLevel = ProtectionLevel.None)]
            public string medicalHistory;
        }
    
    public class Service1 : IService1
        {
            public string GetData(int value)
            {
                return string.Format("You entered: {0}", value);
            }
    
            public AddPatientRecordResponse AddPatientRecord(PatientRecord patient)
            {
                var response = new AddPatientRecordResponse
                                   {
                                       patientName = patient.patientName, 
                                       recordID = patient.recordID, 
                                       status = "Sucess"
                                   };
    
                return response;
            }
        }
    

    Hook into wcf extensibility

    public class MyValidationBeforeInvokeBehavior : Attribute, IOperationBehavior
        {
            public void Validate(OperationDescription operationDescription)
            {
            }
    
            public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
            {
                dispatchOperation.Invoker = new MyValidationBeforeInvoke(dispatchOperation.Invoker);
            }
    
            public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
            {
            }
    
            public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
            {
            }
        }
    

    Custom operation invoker:

    public class MyValidationBeforeInvoke : IOperationInvoker
        {
            private readonly IOperationInvoker _original;
    
            public MyValidationBeforeInvoke(IOperationInvoker original)
            {
                _original = original;
            }
    
            public object[] AllocateInputs()
            {
                return _original.AllocateInputs();
            }
    
            public object Invoke(object instance, object[] inputs, out object[] outputs)
            {
    
                var validator = new ValidatePatientRecord((PatientRecord) inputs[0]);
                if (validator.IsValid())
                {
                    var ret = _original.Invoke(instance, inputs, out outputs);
                    return ret;
                }
                else
                {
                    outputs = new object[] {};
    
                    var patientRecord = (PatientRecord) inputs[0];
    
                    var returnMessage = new AddPatientRecordResponse
                                            {
                                                patientName = patientRecord.patientName,
                                                recordID = patientRecord.recordID,
                                                status = "Validation Failed"
                                            };
                    return returnMessage;    
                }
    
    
            }
    
            public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
            {
               return _original.InvokeBegin(instance, inputs, callback, state);
            }
    
            public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
            {
                return _original.InvokeEnd(instance, out outputs, result);
            }
    
            public bool IsSynchronous
            {
                get { return _original.IsSynchronous; }
            }
        }
    

    The essence is that we never invoke the service call because it will never get there due to the validation error. We can also return what happened to the client.

    Validation class and client call (for completes):

    public class ValidatePatientRecord
        {
            private readonly PatientRecord _patientRecord;
    
            public ValidatePatientRecord(PatientRecord patientRecord)
            {
                _patientRecord = patientRecord;
            }
    
            public bool IsValid()
            {
                return _patientRecord.patientName != "Stack Overflow";
            }
        }
    

    client:

    class Program
        {
            static void Main(string[] args)
            {
                var patient = new PatientRecord { SSN = "123", recordID = Guid.NewGuid(), patientName = "Stack Overflow" };
    
                var proxy = new ServiceReference1.Service1Client();
    
                var result = proxy.AddPatientRecord(patient);
    
                Console.WriteLine(result.status);
                Console.ReadLine();
            }
        }