File: System\ServiceModel\Dispatcher\OperationFormatter.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
namespace System.ServiceModel.Dispatcher
{
    using System.Collections;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Reflection;
    using System.Xml;
    using System.ServiceModel.Diagnostics;
    using System.ServiceModel.Channels;
    using System.Runtime.Diagnostics;
    using System.Runtime;
    using System.Threading;
 
    abstract class OperationFormatter : IClientMessageFormatter, IDispatchMessageFormatter
    {
        MessageDescription replyDescription;
        MessageDescription requestDescription;
        XmlDictionaryString action;
        XmlDictionaryString replyAction;
        protected StreamFormatter requestStreamFormatter, replyStreamFormatter;
        XmlDictionary dictionary;
        string operationName;
        static object[] emptyObjectArray = new object[0];
 
        public OperationFormatter(OperationDescription description, bool isRpc, bool isEncoded)
        {
            Validate(description, isRpc, isEncoded);
            this.requestDescription = description.Messages[0];
            if (description.Messages.Count == 2)
                this.replyDescription = description.Messages[1];
 
            int stringCount = 3 + requestDescription.Body.Parts.Count;
            if (replyDescription != null)
                stringCount += 2 + replyDescription.Body.Parts.Count;
 
            dictionary = new XmlDictionary(stringCount * 2);
            GetActions(description, dictionary, out this.action, out this.replyAction);
            operationName = description.Name;
            requestStreamFormatter = StreamFormatter.Create(requestDescription, operationName, true/*isRequest*/);
            if (replyDescription != null)
                replyStreamFormatter = StreamFormatter.Create(replyDescription, operationName, false/*isResponse*/);
        }
 
        protected abstract void AddHeadersToMessage(Message message, MessageDescription messageDescription, object[] parameters, bool isRequest);
        protected abstract void SerializeBody(XmlDictionaryWriter writer, MessageVersion version, string action, MessageDescription messageDescription, object returnValue, object[] parameters, bool isRequest);
        protected abstract void GetHeadersFromMessage(Message message, MessageDescription messageDescription, object[] parameters, bool isRequest);
        protected abstract object DeserializeBody(XmlDictionaryReader reader, MessageVersion version, string action, MessageDescription messageDescription, object[] parameters, bool isRequest);
 
        protected virtual void WriteBodyAttributes(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
        }
 
        internal string RequestAction
        {
            get
            {
                if (action != null)
                    return action.Value;
                return null;
            }
        }
        internal string ReplyAction
        {
            get
            {
                if (replyAction != null)
                    return replyAction.Value;
                return null;
            }
        }
 
        protected XmlDictionary Dictionary
        {
            get { return dictionary; }
        }
 
        protected string OperationName
        {
            get { return this.operationName; }
        }
 
        protected MessageDescription ReplyDescription
        {
            get { return replyDescription; }
        }
 
        protected MessageDescription RequestDescription
        {
            get { return requestDescription; }
        }
 
        protected XmlDictionaryString AddToDictionary(string s)
        {
            return AddToDictionary(dictionary, s);
        }
 
        public object DeserializeReply(Message message, object[] parameters)
        {
            if (message == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
 
            if (parameters == null)
                throw TraceUtility.ThrowHelperError(new ArgumentNullException("parameters"), message);
 
            try
            {
                object result = null;
                if (replyDescription.IsTypedMessage)
                {
                    object typeMessageInstance = CreateTypedMessageInstance(replyDescription.MessageType);
                    TypedMessageParts typedMessageParts = new TypedMessageParts(typeMessageInstance, replyDescription);
                    object[] parts = new object[typedMessageParts.Count];
 
                    GetPropertiesFromMessage(message, replyDescription, parts);
                    GetHeadersFromMessage(message, replyDescription, parts, false/*isRequest*/);
                    DeserializeBodyContents(message, parts, false/*isRequest*/);
 
                    // copy values into the actual field/properties
                    typedMessageParts.SetTypedMessageParts(parts);
 
                    result = typeMessageInstance;
                }
                else
                {
                    GetPropertiesFromMessage(message, replyDescription, parameters);
                    GetHeadersFromMessage(message, replyDescription, parameters, false/*isRequest*/);
                    result = DeserializeBodyContents(message, parameters, false/*isRequest*/);
                }
                return result;
            }
            catch (XmlException xe)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
                    SR.GetString(SR.SFxErrorDeserializingReplyBodyMore, this.operationName, xe.Message), xe));
            }
            catch (FormatException fe)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
                    SR.GetString(SR.SFxErrorDeserializingReplyBodyMore, this.operationName, fe.Message), fe));
            }
            catch (SerializationException se)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
                    SR.GetString(SR.SFxErrorDeserializingReplyBodyMore, this.operationName, se.Message), se));
            }
        }
 
        private static object CreateTypedMessageInstance(Type messageContractType)
        {
            BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance | BindingFlags.NonPublic;
            try
            {
                object typeMessageInstance = Activator.CreateInstance(
                    messageContractType,
                    bindingFlags,
                    null, emptyObjectArray, null);
                return typeMessageInstance;
            }
            catch (MissingMethodException mme)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxMessageContractRequiresDefaultConstructor, messageContractType.FullName), mme));
            }
        }
 
        public void DeserializeRequest(Message message, object[] parameters)
        {
            if (message == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
 
            if (parameters == null)
                throw TraceUtility.ThrowHelperError(new ArgumentNullException("parameters"), message);
 
            try
            {
                if (requestDescription.IsTypedMessage)
                {
                    object typeMessageInstance = CreateTypedMessageInstance(requestDescription.MessageType);
                    TypedMessageParts typedMessageParts = new TypedMessageParts(typeMessageInstance, requestDescription);
                    object[] parts = new object[typedMessageParts.Count];
 
                    GetPropertiesFromMessage(message, requestDescription, parts);
                    GetHeadersFromMessage(message, requestDescription, parts, true/*isRequest*/);
                    DeserializeBodyContents(message, parts, true/*isRequest*/);
 
                    // copy values into the actual field/properties
                    typedMessageParts.SetTypedMessageParts(parts);
 
                    parameters[0] = typeMessageInstance;
                }
                else
                {
                    GetPropertiesFromMessage(message, requestDescription, parameters);
                    GetHeadersFromMessage(message, requestDescription, parameters, true/*isRequest*/);
                    DeserializeBodyContents(message, parameters, true/*isRequest*/);
                }
            }
            catch (XmlException xe)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    OperationFormatter.CreateDeserializationFailedFault(
                        SR.GetString(SR.SFxErrorDeserializingRequestBodyMore, this.operationName, xe.Message), 
                        xe));
            }
            catch (FormatException fe)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    OperationFormatter.CreateDeserializationFailedFault(
                        SR.GetString(SR.SFxErrorDeserializingRequestBodyMore, this.operationName, fe.Message), 
                        fe));
            }
            catch (SerializationException se)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
                    SR.GetString(SR.SFxErrorDeserializingRequestBodyMore, this.operationName, se.Message), 
                    se));
            }
        }
 
        object DeserializeBodyContents(Message message, object[] parameters, bool isRequest)
        {
            MessageDescription messageDescription;
            StreamFormatter streamFormatter;
 
            SetupStreamAndMessageDescription(isRequest, out streamFormatter, out messageDescription);
 
            if (streamFormatter != null)
            {
                object retVal = null;
                streamFormatter.Deserialize(parameters, ref retVal, message);
                return retVal;
            }
 
            if (message.IsEmpty)
            {
                return null;
            }
            else
            {
                XmlDictionaryReader reader = message.GetReaderAtBodyContents();
                using (reader)
                {
                    object body = DeserializeBody(reader, message.Version, RequestAction, messageDescription, parameters, isRequest);
                    message.ReadFromBodyContentsToEnd(reader);
                    return body;
                }
            }
        }
 
        public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
        {
            object[] parts = null;
 
            if (messageVersion == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageVersion");
 
            if (parameters == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parameters");
            if (requestDescription.IsTypedMessage)
            {
                TypedMessageParts typedMessageParts = new TypedMessageParts(parameters[0], requestDescription);
 
                // copy values from the actual field/properties
                parts = new object[typedMessageParts.Count];
                typedMessageParts.GetTypedMessageParts(parts);
            }
            else
            {
                parts = parameters;
            }
            Message msg = new OperationFormatterMessage(this, messageVersion,
                action == null ? null : ActionHeader.Create(action, messageVersion.Addressing),
                parts, null, true/*isRequest*/);
            AddPropertiesToMessage(msg, requestDescription, parts);
            AddHeadersToMessage(msg, requestDescription, parts, true /*isRequest*/);
 
            return msg;
        }
 
        public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
        {
            object[] parts = null;
            object resultPart = null;
 
            if (messageVersion == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageVersion");
 
            if (parameters == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parameters");
 
            if (replyDescription.IsTypedMessage)
            {
                // If the response is a typed message then it must 
                // me the response (as opposed to an out param).  We will
                // serialize the response in the exact same way that we
                // would serialize a bunch of outs (with no return value).
 
                TypedMessageParts typedMessageParts = new TypedMessageParts(result, replyDescription);
 
                // make a copy of the list so that we have the actual values of the field/properties
                parts = new object[typedMessageParts.Count];
                typedMessageParts.GetTypedMessageParts(parts);
 
                resultPart = null;
            }
            else
            {
                parts = parameters;
                resultPart = result;
            }
 
            Message msg = new OperationFormatterMessage(this, messageVersion,
                replyAction == null ? null : ActionHeader.Create(replyAction, messageVersion.Addressing),
                parts, resultPart, false/*isRequest*/);
            AddPropertiesToMessage(msg, replyDescription, parts);
            AddHeadersToMessage(msg, replyDescription, parts, false /*isRequest*/);
            return msg;
        }
 
        void SetupStreamAndMessageDescription(bool isRequest, out StreamFormatter streamFormatter, out MessageDescription messageDescription)
        {
            if (isRequest)
            {
                streamFormatter = requestStreamFormatter;
                messageDescription = requestDescription;
            }
            else
            {
                streamFormatter = replyStreamFormatter;
                messageDescription = replyDescription;
            }
        }
 
        void SerializeBodyContents(XmlDictionaryWriter writer, MessageVersion version, object[] parameters, object returnValue, bool isRequest)
        {
            MessageDescription messageDescription;
            StreamFormatter streamFormatter;
 
            SetupStreamAndMessageDescription(isRequest, out streamFormatter, out messageDescription);
 
            if (streamFormatter != null)
            {
                streamFormatter.Serialize(writer, parameters, returnValue);
                return;
            }
 
            SerializeBody(writer, version, RequestAction, messageDescription, returnValue, parameters, isRequest);
        }
 
        IAsyncResult BeginSerializeBodyContents(XmlDictionaryWriter writer, MessageVersion version, object[] parameters, object returnValue, bool isRequest,
            AsyncCallback callback, object state)
        {
            return new SerializeBodyContentsAsyncResult(this, writer, version, parameters, returnValue, isRequest, callback, state);
        }
 
        void EndSerializeBodyContents(IAsyncResult result)
        {
            SerializeBodyContentsAsyncResult.End(result);
        }
 
        class SerializeBodyContentsAsyncResult : AsyncResult
        {
            static AsyncCompletion handleEndSerializeBodyContents = new AsyncCompletion(HandleEndSerializeBodyContents);
 
            StreamFormatter streamFormatter;
 
            internal SerializeBodyContentsAsyncResult(OperationFormatter operationFormatter, XmlDictionaryWriter writer, MessageVersion version, object[] parameters,
                object returnValue, bool isRequest, AsyncCallback callback, object state)
                : base(callback, state)
            {
                bool completeSelf = true;
 
                MessageDescription messageDescription;
                StreamFormatter streamFormatter;
 
                operationFormatter.SetupStreamAndMessageDescription(isRequest, out streamFormatter, out messageDescription);
 
                if (streamFormatter != null)
                {
                    this.streamFormatter = streamFormatter;
                    IAsyncResult result = streamFormatter.BeginSerialize(writer, parameters, returnValue, PrepareAsyncCompletion(handleEndSerializeBodyContents), this);
                    completeSelf = SyncContinue(result);
                }
                else
                {
                    operationFormatter.SerializeBody(writer, version, operationFormatter.RequestAction, messageDescription, returnValue, parameters, isRequest);
                    completeSelf = true;
                }
 
 
                if (completeSelf)
                {
                    Complete(true);
                }
            }
 
 
            static bool HandleEndSerializeBodyContents(IAsyncResult result)
            {
                SerializeBodyContentsAsyncResult thisPtr = (SerializeBodyContentsAsyncResult)result.AsyncState;
                thisPtr.streamFormatter.EndSerialize(result);
                return true;
            }
 
            public static void End(IAsyncResult result)
            {
                AsyncResult.End<SerializeBodyContentsAsyncResult>(result);
            }
        }
 
        void AddPropertiesToMessage(Message message, MessageDescription messageDescription, object[] parameters)
        {
            if (messageDescription.Properties.Count > 0)
            {
                AddPropertiesToMessageCore(message, messageDescription, parameters);
            }
        }
 
        void AddPropertiesToMessageCore(Message message, MessageDescription messageDescription, object[] parameters)
        {
            MessageProperties properties = message.Properties;
            MessagePropertyDescriptionCollection propertyDescriptions = messageDescription.Properties;
            for (int i = 0; i < propertyDescriptions.Count; i++)
            {
                MessagePropertyDescription propertyDescription = propertyDescriptions[i];
                object parameter = parameters[propertyDescription.Index];
                if (null != parameter)
                    properties.Add(propertyDescription.Name, parameter);
            }
        }
 
        void GetPropertiesFromMessage(Message message, MessageDescription messageDescription, object[] parameters)
        {
            if (messageDescription.Properties.Count > 0)
            {
                GetPropertiesFromMessageCore(message, messageDescription, parameters);
            }
        }
 
        void GetPropertiesFromMessageCore(Message message, MessageDescription messageDescription, object[] parameters)
        {
            MessageProperties properties = message.Properties;
            MessagePropertyDescriptionCollection propertyDescriptions = messageDescription.Properties;
            for (int i = 0; i < propertyDescriptions.Count; i++)
            {
                MessagePropertyDescription propertyDescription = propertyDescriptions[i];
                if (properties.ContainsKey(propertyDescription.Name))
                {
                    parameters[propertyDescription.Index] = properties[propertyDescription.Name];
                }
            }
        }
 
        internal static object GetContentOfMessageHeaderOfT(MessageHeaderDescription headerDescription, object parameterValue, out bool mustUnderstand, out bool relay, out string actor)
        {
            actor = headerDescription.Actor;
            mustUnderstand = headerDescription.MustUnderstand;
            relay = headerDescription.Relay;
 
            if (headerDescription.TypedHeader && parameterValue != null)
                parameterValue = TypedHeaderManager.GetContent(headerDescription.Type, parameterValue, out mustUnderstand, out relay, out actor);
            return parameterValue;
        }
 
        internal static bool IsValidReturnValue(MessagePartDescription returnValue)
        {
            return (returnValue != null) && (returnValue.Type != typeof(void));
        }
 
        internal static XmlDictionaryString AddToDictionary(XmlDictionary dictionary, string s)
        {
            XmlDictionaryString dictionaryString;
            if (!dictionary.TryLookup(s, out dictionaryString))
            {
                dictionaryString = dictionary.Add(s);
            }
            return dictionaryString;
        }
 
        internal static void Validate(OperationDescription operation, bool isRpc, bool isEncoded)
        {
            if (isEncoded && !isRpc)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxDocEncodedNotSupported, operation.Name)));
            }
 
            bool hasVoid = false;
            bool hasTypedOrUntypedMessage = false;
            bool hasParameter = false;
            for (int i = 0; i < operation.Messages.Count; i++)
            {
                MessageDescription message = operation.Messages[i];
                if (message.IsTypedMessage || message.IsUntypedMessage)
                {
                    if (isRpc && operation.IsValidateRpcWrapperName)
                    {
                        if (!isEncoded)
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxTypedMessageCannotBeRpcLiteral, operation.Name)));
 
                    }
                    hasTypedOrUntypedMessage = true;
                }
                else if (message.IsVoid)
                    hasVoid = true;
                else
                    hasParameter = true;
            }
            if (hasParameter && hasTypedOrUntypedMessage)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxTypedOrUntypedMessageCannotBeMixedWithParameters, operation.Name)));
            if (isRpc && hasTypedOrUntypedMessage && hasVoid)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxTypedOrUntypedMessageCannotBeMixedWithVoidInRpc, operation.Name)));
        }
 
        internal static void GetActions(OperationDescription description, XmlDictionary dictionary, out XmlDictionaryString action, out XmlDictionaryString replyAction)
        {
            string actionString, replyActionString;
            actionString = description.Messages[0].Action;
            if (actionString == MessageHeaders.WildcardAction)
                actionString = null;
            if (!description.IsOneWay)
                replyActionString = description.Messages[1].Action;
            else
                replyActionString = null;
            if (replyActionString == MessageHeaders.WildcardAction)
                replyActionString = null;
            action = replyAction = null;
            if (actionString != null)
                action = AddToDictionary(dictionary, actionString);
            if (replyActionString != null)
                replyAction = AddToDictionary(dictionary, replyActionString);
        }
 
        internal static NetDispatcherFaultException CreateDeserializationFailedFault(string reason, Exception innerException)
        {
            reason = SR.GetString(SR.SFxDeserializationFailed1, reason);
            FaultCode code = new FaultCode(FaultCodeConstants.Codes.DeserializationFailed, FaultCodeConstants.Namespaces.NetDispatch);
            code = FaultCode.CreateSenderFaultCode(code);
            return new NetDispatcherFaultException(reason, code, innerException);
        }
 
        internal static void TraceAndSkipElement(XmlReader xmlReader)
        {
            if (DiagnosticUtility.ShouldTraceVerbose)
            {
                TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.ElementIgnored, SR.GetString(SR.SFxTraceCodeElementIgnored), new StringTraceRecord("Element", xmlReader.NamespaceURI + ":" + xmlReader.LocalName));
            }
            xmlReader.Skip();
        }
 
        class TypedMessageParts
        {
            object instance;
            MemberInfo[] members;
 
            public TypedMessageParts(object instance, MessageDescription description)
            {
                if (description == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("description"));
                }
 
                if (instance == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(SR.GetString(SR.SFxTypedMessageCannotBeNull, description.Action)));
                }
 
                members = new MemberInfo[description.Body.Parts.Count + description.Properties.Count + description.Headers.Count];
 
                foreach (MessagePartDescription part in description.Headers)
                    members[part.Index] = part.MemberInfo;
 
                foreach (MessagePartDescription part in description.Properties)
                    members[part.Index] = part.MemberInfo;
 
                foreach (MessagePartDescription part in description.Body.Parts)
                    members[part.Index] = part.MemberInfo;
 
                this.instance = instance;
            }
 
            object GetValue(int index)
            {
                MemberInfo memberInfo = members[index];
                if (memberInfo.MemberType == MemberTypes.Property)
                {
                    return ((PropertyInfo)members[index]).GetValue(instance, null);
                }
                else
                {
                    return ((FieldInfo)members[index]).GetValue(instance);
                }
            }
 
            void SetValue(object value, int index)
            {
                MemberInfo memberInfo = members[index];
                if (memberInfo.MemberType == MemberTypes.Property)
                {
                    ((PropertyInfo)members[index]).SetValue(instance, value, null);
                }
                else
                {
                    ((FieldInfo)members[index]).SetValue(instance, value);
                }
            }
 
            internal void GetTypedMessageParts(object[] values)
            {
                for (int i = 0; i < this.members.Length; i++)
                {
                    values[i] = GetValue(i);
                }
            }
 
            internal void SetTypedMessageParts(object[] values)
            {
                for (int i = 0; i < this.members.Length; i++)
                {
                    SetValue(values[i], i);
                }
            }
 
            internal int Count
            {
                get { return this.members.Length; }
            }
        }
 
        internal class OperationFormatterMessage : BodyWriterMessage
        {
            OperationFormatter operationFormatter;
            public OperationFormatterMessage(OperationFormatter operationFormatter, MessageVersion version, ActionHeader action,
               object[] parameters, object returnValue, bool isRequest)
                : base(version, action, new OperationFormatterBodyWriter(operationFormatter, version, parameters, returnValue, isRequest))
            {
                this.operationFormatter = operationFormatter;
            }
 
 
            public OperationFormatterMessage(MessageVersion version, string action, BodyWriter bodyWriter) : base(version, action, bodyWriter) { }
 
            OperationFormatterMessage(MessageHeaders headers, KeyValuePair<string, object>[] properties, OperationFormatterBodyWriter bodyWriter)
                : base(headers, properties, bodyWriter)
            {
                operationFormatter = bodyWriter.OperationFormatter;
            }
 
            protected override void OnWriteStartBody(XmlDictionaryWriter writer)
            {
                base.OnWriteStartBody(writer);
                operationFormatter.WriteBodyAttributes(writer, this.Version);
            }
 
            protected override MessageBuffer OnCreateBufferedCopy(int maxBufferSize)
            {
                BodyWriter bufferedBodyWriter;
                if (BodyWriter.IsBuffered)
                {
                    bufferedBodyWriter = base.BodyWriter;
                }
                else
                {
                    bufferedBodyWriter = base.BodyWriter.CreateBufferedCopy(maxBufferSize);
                }
                KeyValuePair<string, object>[] properties = new KeyValuePair<string, object>[base.Properties.Count];
                ((ICollection<KeyValuePair<string, object>>)base.Properties).CopyTo(properties, 0);
                return new OperationFormatterMessageBuffer(base.Headers, properties, bufferedBodyWriter);
            }
 
            class OperationFormatterBodyWriter : BodyWriter
            {
                bool isRequest;
                OperationFormatter operationFormatter;
                object[] parameters;
                object returnValue;
                MessageVersion version;
                bool onBeginWriteBodyContentsCalled;
 
                public OperationFormatterBodyWriter(OperationFormatter operationFormatter, MessageVersion version,
                    object[] parameters, object returnValue, bool isRequest)
                    : base(AreParametersBuffered(isRequest, operationFormatter))
                {
                    this.parameters = parameters;
                    this.returnValue = returnValue;
                    this.isRequest = isRequest;
                    this.operationFormatter = operationFormatter;
                    this.version = version;
                }
 
                object ThisLock
                {
                    get { return this; }
                }
 
                static bool AreParametersBuffered(bool isRequest, OperationFormatter operationFormatter)
                {
                    StreamFormatter streamFormatter = isRequest ? operationFormatter.requestStreamFormatter : operationFormatter.replyStreamFormatter;
                    return streamFormatter == null;
                }
 
                protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
                {
                    lock (ThisLock)
                    {
                        this.operationFormatter.SerializeBodyContents(writer, this.version, this.parameters, this.returnValue, this.isRequest);
                    }
                }
 
                protected override IAsyncResult OnBeginWriteBodyContents(XmlDictionaryWriter writer, AsyncCallback callback, object state)
                {
                    Fx.Assert(!onBeginWriteBodyContentsCalled, "OnBeginWriteBodyContents has already been called");
                    this.onBeginWriteBodyContentsCalled = true;
                    return new OnWriteBodyContentsAsyncResult(this, writer, callback, state);
                }
 
                protected override void OnEndWriteBodyContents(IAsyncResult result)
                {
                    OnWriteBodyContentsAsyncResult.End(result);
                }
 
                internal OperationFormatter OperationFormatter
                {
                    get { return operationFormatter; }
                }
 
                class OnWriteBodyContentsAsyncResult : AsyncResult
                {
                    static AsyncCompletion handleEndOnWriteBodyContents = new AsyncCompletion(HandleEndOnWriteBodyContents);
 
                    OperationFormatter operationFormatter;
 
                    internal OnWriteBodyContentsAsyncResult(OperationFormatterBodyWriter operationFormatterBodyWriter, XmlDictionaryWriter writer, AsyncCallback callback, object state)
                        : base(callback, state)
                    {
                        bool completeSelf = true;
                        this.operationFormatter = operationFormatterBodyWriter.OperationFormatter;
 
                        IAsyncResult result = this.operationFormatter.BeginSerializeBodyContents(writer, operationFormatterBodyWriter.version,
                            operationFormatterBodyWriter.parameters, operationFormatterBodyWriter.returnValue, operationFormatterBodyWriter.isRequest,
                            PrepareAsyncCompletion(handleEndOnWriteBodyContents), this);
                        completeSelf = SyncContinue(result);
 
                        if (completeSelf)
                        {
                            Complete(true);
                        }
                    }
 
                    static bool HandleEndOnWriteBodyContents(IAsyncResult result)
                    {
                        OnWriteBodyContentsAsyncResult thisPtr = (OnWriteBodyContentsAsyncResult)result.AsyncState;
                        thisPtr.operationFormatter.EndSerializeBodyContents(result);
                        return true;
                    }
 
                    public static void End(IAsyncResult result)
                    {
                        AsyncResult.End<OnWriteBodyContentsAsyncResult>(result);
                    }
                }
            }
 
            class OperationFormatterMessageBuffer : BodyWriterMessageBuffer
            {
                public OperationFormatterMessageBuffer(MessageHeaders headers,
                    KeyValuePair<string, object>[] properties, BodyWriter bodyWriter)
                    : base(headers, properties, bodyWriter)
                {
                }
 
                public override Message CreateMessage()
                {
                    OperationFormatterBodyWriter operationFormatterBodyWriter = base.BodyWriter as OperationFormatterBodyWriter;
                    if (operationFormatterBodyWriter == null)
                        return base.CreateMessage();
                    lock (ThisLock)
                    {
                        if (base.Closed)
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException());
                        return new OperationFormatterMessage(base.Headers, base.Properties, operationFormatterBodyWriter);
                    }
                }
            }
        }
 
        internal abstract class OperationFormatterHeader : MessageHeader
        {
            protected MessageHeader innerHeader; //use innerHeader to handle versionSupported, actor/role handling etc.
            protected OperationFormatter operationFormatter;
            protected MessageVersion version;
 
            public OperationFormatterHeader(OperationFormatter operationFormatter, MessageVersion version, string name, string ns, bool mustUnderstand, string actor, bool relay)
            {
                this.operationFormatter = operationFormatter;
                this.version = version;
                if (actor != null)
                    innerHeader = MessageHeader.CreateHeader(name, ns, null/*headerValue*/, mustUnderstand, actor, relay);
                else
                    innerHeader = MessageHeader.CreateHeader(name, ns, null/*headerValue*/, mustUnderstand, "", relay);
            }
 
 
            public override bool IsMessageVersionSupported(MessageVersion messageVersion)
            {
                return innerHeader.IsMessageVersionSupported(messageVersion);
            }
 
 
            public override string Name
            {
                get { return innerHeader.Name; }
            }
 
            public override string Namespace
            {
                get { return innerHeader.Namespace; }
            }
 
            public override bool MustUnderstand
            {
                get { return innerHeader.MustUnderstand; }
            }
 
            public override bool Relay
            {
                get { return innerHeader.Relay; }
            }
 
            public override string Actor
            {
                get { return innerHeader.Actor; }
            }
 
            protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion)
            {
                //Prefix needed since there may be xsi:type attribute at toplevel with qname value where ns = ""
                writer.WriteStartElement((this.Namespace == null || this.Namespace.Length == 0) ? string.Empty : "h", this.Name, this.Namespace);
                OnWriteHeaderAttributes(writer, messageVersion);
            }
 
            protected virtual void OnWriteHeaderAttributes(XmlDictionaryWriter writer, MessageVersion messageVersion)
            {
                base.WriteHeaderAttributes(writer, messageVersion);
            }
        }
 
        internal class XmlElementMessageHeader : OperationFormatterHeader
        {
            protected XmlElement headerValue;
            public XmlElementMessageHeader(OperationFormatter operationFormatter, MessageVersion version, string name, string ns, bool mustUnderstand, string actor, bool relay, XmlElement headerValue) :
                base(operationFormatter, version, name, ns, mustUnderstand, actor, relay)
            {
                this.headerValue = headerValue;
            }
 
            protected override void OnWriteHeaderAttributes(XmlDictionaryWriter writer, MessageVersion messageVersion)
            {
                base.WriteHeaderAttributes(writer, messageVersion);
                XmlDictionaryReader nodeReader = XmlDictionaryReader.CreateDictionaryReader(new XmlNodeReader(headerValue));
                nodeReader.MoveToContent();
                writer.WriteAttributes(nodeReader, false);
            }
 
            protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
            {
                headerValue.WriteContentTo(writer);
            }
        }
        internal struct QName
        {
            internal string Name;
            internal string Namespace;
            internal QName(string name, string ns)
            {
                Name = name;
                Namespace = ns;
            }
        }
        internal class QNameComparer : IEqualityComparer<QName>
        {
            static internal QNameComparer Singleton = new QNameComparer();
            QNameComparer() { }
 
            public bool Equals(QName x, QName y)
            {
                return x.Name == y.Name && x.Namespace == y.Namespace;
            }
 
            public int GetHashCode(QName obj)
            {
                return obj.Name.GetHashCode();
            }
        }
        internal class MessageHeaderDescriptionTable : Dictionary<QName, MessageHeaderDescription>
        {
            internal MessageHeaderDescriptionTable() : base(QNameComparer.Singleton) { }
            internal void Add(string name, string ns, MessageHeaderDescription message)
            {
                base.Add(new QName(name, ns), message);
            }
            internal MessageHeaderDescription Get(string name, string ns)
            {
                MessageHeaderDescription message;
                if (base.TryGetValue(new QName(name, ns), out message))
                    return message;
                return null;
            }
        }
    }
}