File: System\ServiceModel\Description\TypeLoader.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
 
namespace System.ServiceModel.Description
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Reflection;
    using System.Runtime;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Dispatcher;
    using System.Xml;
 
    class TypeLoader
    {
        static Type[] messageContractMemberAttributes = {
            typeof(MessageHeaderAttribute),
            typeof(MessageBodyMemberAttribute), 
            typeof(MessagePropertyAttribute),
        };
 
        static Type[] formatterAttributes = {
            typeof(XmlSerializerFormatAttribute),
            typeof(DataContractFormatAttribute)
        };
 
        static Type[] knownTypesMethodParamType = new Type[] { typeof(ICustomAttributeProvider) };
 
        internal static DataContractFormatAttribute DefaultDataContractFormatAttribute = new DataContractFormatAttribute();
        internal static XmlSerializerFormatAttribute DefaultXmlSerializerFormatAttribute = new XmlSerializerFormatAttribute();
 
        static readonly Type OperationContractAttributeType = typeof(OperationContractAttribute);
 
        internal const string ReturnSuffix = "Result";
        internal const string ResponseSuffix = "Response";
        internal const string FaultSuffix = "Fault";
        internal const BindingFlags DefaultBindingFlags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public;
 
        readonly object thisLock;
        readonly Dictionary<Type, ContractDescription> contracts;
        readonly Dictionary<Type, MessageDescriptionItems> messages;
 
        public TypeLoader()
        {
            this.thisLock = new object();
            this.contracts = new Dictionary<Type, ContractDescription>();
            this.messages = new Dictionary<Type, MessageDescriptionItems>();
        }
 
        [SuppressMessage(FxCop.Category.Usage, "CA2301:EmbeddableTypesInContainersRule", MessageId = "contracts", Justification = "No need to support type equivalence here.")]
        ContractDescription LoadContractDescriptionHelper(Type contractType, Type serviceType, object serviceImplementation)
        {
            ContractDescription contractDescription;
            if (contractType == typeof(IOutputChannel))
            {
                contractDescription = LoadOutputChannelContractDescription();
            }
            else if (contractType == typeof(IRequestChannel))
            {
                contractDescription = LoadRequestChannelContractDescription();
            }
            else
            {
                ServiceContractAttribute actualContractAttribute;
                Type actualContractType = ServiceReflector.GetContractTypeAndAttribute(contractType, out actualContractAttribute);
                lock (this.thisLock)
                {
                    if (!contracts.TryGetValue(actualContractType, out contractDescription))
                    {
                        EnsureNoInheritanceWithContractClasses(actualContractType);
                        EnsureNoOperationContractsOnNonServiceContractTypes(actualContractType);
                        ContractReflectionInfo reflectionInfo;
                        contractDescription = CreateContractDescription(actualContractAttribute, actualContractType, serviceType, out reflectionInfo, serviceImplementation);
                        // IContractBehaviors
                        if (serviceImplementation != null && serviceImplementation is IContractBehavior)
                        {
                            contractDescription.Behaviors.Add((IContractBehavior)serviceImplementation);
                        }
                        if (serviceType != null)
                        {
                            UpdateContractDescriptionWithAttributesFromServiceType(contractDescription, serviceType);
                            foreach (ContractDescription inheritedContract in contractDescription.GetInheritedContracts())
                            {
                                UpdateContractDescriptionWithAttributesFromServiceType(inheritedContract, serviceType);
                            }
                        }
                        UpdateOperationsWithInterfaceAttributes(contractDescription, reflectionInfo);
                        AddBehaviors(contractDescription, serviceType, false, reflectionInfo);
 
                        this.contracts.Add(actualContractType, contractDescription);
                    }
                }
            }
            return contractDescription;
        }
 
        void EnsureNoInheritanceWithContractClasses(Type actualContractType)
        {
            if (actualContractType.IsClass)
            {
                // we only need to check base _classes_ here, the check for interfaces happens elsewhere
                for (Type service = actualContractType.BaseType; service != null; service = service.BaseType)
                {
                    if (ServiceReflector.GetSingleAttribute<ServiceContractAttribute>(service) != null)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                            SR.GetString(SR.SFxContractInheritanceRequiresInterfaces, actualContractType, service)));
                    }
                }
            }
        }
 
        void EnsureNoOperationContractsOnNonServiceContractTypes(Type actualContractType)
        {
            foreach (Type t in actualContractType.GetInterfaces())
            {
                EnsureNoOperationContractsOnNonServiceContractTypes_Helper(t);
            }
            for (Type u = actualContractType.BaseType; u != null; u = u.BaseType)
            {
                EnsureNoOperationContractsOnNonServiceContractTypes_Helper(u);
            }
        }
 
        void EnsureNoOperationContractsOnNonServiceContractTypes_Helper(Type aParentType)
        {
            // if not [ServiceContract]
            if (ServiceReflector.GetSingleAttribute<ServiceContractAttribute>(aParentType) == null)
            {
                foreach (MethodInfo methodInfo in aParentType.GetMethods(DefaultBindingFlags))
                {
                    // but does have an OperationContractAttribute
                    Type operationContractProviderType = ServiceReflector.GetOperationContractProviderType(methodInfo);
                    if (operationContractProviderType != null)
                    {
                        if (operationContractProviderType == OperationContractAttributeType)
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
                                SR.SFxOperationContractOnNonServiceContract, methodInfo.Name, aParentType.Name)));
                        }
                        else
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
                                SR.SFxOperationContractProviderOnNonServiceContract, operationContractProviderType.Name, methodInfo.Name, aParentType.Name)));
                        }
                    }
                }
            }
        }
 
        public ContractDescription LoadContractDescription(Type contractType)
        {
            Fx.Assert(contractType != null, "");
 
            return LoadContractDescriptionHelper(contractType, null, null);
        }
 
        public ContractDescription LoadContractDescription(Type contractType, Type serviceType)
        {
            Fx.Assert(contractType != null, "");
            Fx.Assert(serviceType != null, "");
 
            return LoadContractDescriptionHelper(contractType, serviceType, null);
        }
 
        public ContractDescription LoadContractDescription(Type contractType, Type serviceType, object serviceImplementation)
        {
            Fx.Assert(contractType != null, "");
            Fx.Assert(serviceType != null, "");
            Fx.Assert(serviceImplementation != null, "");
 
            return LoadContractDescriptionHelper(contractType, serviceType, serviceImplementation);
        }
 
        ContractDescription LoadOutputChannelContractDescription()
        {
            Type channelType = typeof(IOutputChannel);
            XmlQualifiedName contractName = NamingHelper.GetContractName(channelType, null, NamingHelper.MSNamespace);
            ContractDescription contract = new ContractDescription(contractName.Name, contractName.Namespace);
            contract.ContractType = channelType;
            contract.ConfigurationName = channelType.FullName;
            contract.SessionMode = SessionMode.NotAllowed;
            OperationDescription operation = new OperationDescription("Send", contract);
            MessageDescription message = new MessageDescription(MessageHeaders.WildcardAction, MessageDirection.Input);
            operation.Messages.Add(message);
            contract.Operations.Add(operation);
            return contract;
        }
 
        ContractDescription LoadRequestChannelContractDescription()
        {
            Type channelType = typeof(IRequestChannel);
            XmlQualifiedName contractName = NamingHelper.GetContractName(channelType, null, NamingHelper.MSNamespace);
            ContractDescription contract = new ContractDescription(contractName.Name, contractName.Namespace);
            contract.ContractType = channelType;
            contract.ConfigurationName = channelType.FullName;
            contract.SessionMode = SessionMode.NotAllowed;
            OperationDescription operation = new OperationDescription("Request", contract);
            MessageDescription request = new MessageDescription(MessageHeaders.WildcardAction, MessageDirection.Input);
            MessageDescription reply = new MessageDescription(MessageHeaders.WildcardAction, MessageDirection.Output);
            operation.Messages.Add(request);
            operation.Messages.Add(reply);
            contract.Operations.Add(operation);
            return contract;
        }
 
        void AddBehaviors(ContractDescription contractDesc, Type implType, bool implIsCallback, ContractReflectionInfo reflectionInfo)
        {
            ServiceContractAttribute contractAttr = ServiceReflector.GetRequiredSingleAttribute<ServiceContractAttribute>(reflectionInfo.iface);
            for (int i = 0; i < contractDesc.Operations.Count; i++)
            {
                OperationDescription operationDescription = contractDesc.Operations[i];
                bool isInherited = operationDescription.DeclaringContract != contractDesc;
                if (!isInherited)
                {
                    operationDescription.Behaviors.Add(new OperationInvokerBehavior());
                }
            }
            contractDesc.Behaviors.Add(new OperationSelectorBehavior());
 
            for (int i = 0; i < contractDesc.Operations.Count; i++)
            {
                OperationDescription opDesc = contractDesc.Operations[i];
                bool isInherited = opDesc.DeclaringContract != contractDesc;
                Type targetIface = implIsCallback ? opDesc.DeclaringContract.CallbackContractType : opDesc.DeclaringContract.ContractType;
 
                if (implType == null && !isInherited)
                {
                    KeyedByTypeCollection<IOperationBehavior> toAdd =
                        GetIOperationBehaviorAttributesFromType(opDesc, targetIface, null);
                    for (int j = 0; j < toAdd.Count; j++)
                    {
                        opDesc.Behaviors.Add(toAdd[j]);
                    }
                }
                else
                {
                    // look for IOperationBehaviors on implementation methods in service class hierarchy
                    ApplyServiceInheritance<IOperationBehavior, KeyedByTypeCollection<IOperationBehavior>>(
                        implType, opDesc.Behaviors,
                        delegate(Type currentType, KeyedByTypeCollection<IOperationBehavior> behaviors)
                        {
                            KeyedByTypeCollection<IOperationBehavior> toAdd =
                                GetIOperationBehaviorAttributesFromType(opDesc, targetIface, currentType);
                            for (int j = 0; j < toAdd.Count; j++)
                            {
                                behaviors.Add(toAdd[j]);
                            }
                        });
                    // then look for IOperationBehaviors on interface type
                    if (!isInherited)
                    {
                        AddBehaviorsAtOneScope<IOperationBehavior, KeyedByTypeCollection<IOperationBehavior>>(
                            targetIface, opDesc.Behaviors,
                            delegate(Type currentType, KeyedByTypeCollection<IOperationBehavior> behaviors)
                            {
                                KeyedByTypeCollection<IOperationBehavior> toAdd =
                                    GetIOperationBehaviorAttributesFromType(opDesc, targetIface, null);
                                for (int j = 0; j < toAdd.Count; j++)
                                {
                                    behaviors.Add(toAdd[j]);
                                }
                            });
                    }
                }
            }
 
            for (int i = 0; i < contractDesc.Operations.Count; i++)
            {
                OperationDescription opDesc = contractDesc.Operations[i];
                OperationBehaviorAttribute operationBehavior = opDesc.Behaviors.Find<OperationBehaviorAttribute>();
                if (operationBehavior == null)
                {
                    operationBehavior = new OperationBehaviorAttribute();
                    opDesc.Behaviors.Add(operationBehavior);
                }
            }
 
            Type targetInterface = implIsCallback ? reflectionInfo.callbackiface : reflectionInfo.iface;
            AddBehaviorsAtOneScope<IContractBehavior, KeyedByTypeCollection<IContractBehavior>>(targetInterface, contractDesc.Behaviors,
                GetIContractBehaviorsFromInterfaceType);
 
            bool hasXmlSerializerMethod = false;
            for (int i = 0; i < contractDesc.Operations.Count; i++)
            {
                OperationDescription operationDescription = contractDesc.Operations[i];
                bool isInherited = operationDescription.DeclaringContract != contractDesc;
                MethodInfo opMethod = operationDescription.OperationMethod;
                Attribute formattingAttribute = GetFormattingAttribute(opMethod,
                                                    GetFormattingAttribute(operationDescription.DeclaringContract.ContractType,
                                                        DefaultDataContractFormatAttribute));
                DataContractFormatAttribute dataContractFormatAttribute = formattingAttribute as DataContractFormatAttribute;
                if (dataContractFormatAttribute != null)
                {
                    if (!isInherited)
                    {
                        operationDescription.Behaviors.Add(new DataContractSerializerOperationBehavior(operationDescription, dataContractFormatAttribute, true));
                        operationDescription.Behaviors.Add(new DataContractSerializerOperationGenerator());
                    }
                }
                else if (formattingAttribute != null && formattingAttribute is XmlSerializerFormatAttribute)
                {
                    hasXmlSerializerMethod = true;
                }
            }
            if (hasXmlSerializerMethod)
            {
                XmlSerializerOperationBehavior.AddBuiltInBehaviors(contractDesc);
            }
        }
 
        void GetIContractBehaviorsFromInterfaceType(Type interfaceType, KeyedByTypeCollection<IContractBehavior> behaviors)
        {
            object[] ifaceAttributes = ServiceReflector.GetCustomAttributes(interfaceType, typeof(IContractBehavior), false);
            for (int i = 0; i < ifaceAttributes.Length; i++)
            {
                IContractBehavior behavior = (IContractBehavior)ifaceAttributes[i];
                behaviors.Add(behavior);
            }
        }
 
        static void UpdateContractDescriptionWithAttributesFromServiceType(ContractDescription description, Type serviceType)
        {
            ApplyServiceInheritance<IContractBehavior, KeyedByTypeCollection<IContractBehavior>>(
                serviceType, description.Behaviors,
                delegate(Type currentType, KeyedByTypeCollection<IContractBehavior> behaviors)
                {
 
                    foreach (IContractBehavior iContractBehavior in ServiceReflector.GetCustomAttributes(currentType, typeof(IContractBehavior), false))
                    {
                        IContractBehaviorAttribute iContractBehaviorAttribute = iContractBehavior as IContractBehaviorAttribute;
                        if (iContractBehaviorAttribute == null
                            || (iContractBehaviorAttribute.TargetContract == null)
                            || (iContractBehaviorAttribute.TargetContract == description.ContractType))
                        {
                            behaviors.Add(iContractBehavior);
                        }
                    }
                });
        }
 
        void UpdateOperationsWithInterfaceAttributes(ContractDescription contractDesc, ContractReflectionInfo reflectionInfo)
        {
            object[] customAttributes = ServiceReflector.GetCustomAttributes(reflectionInfo.iface, typeof(ServiceKnownTypeAttribute), false);
            IEnumerable<Type> knownTypes = GetKnownTypes(customAttributes, reflectionInfo.iface);
            foreach (Type knownType in knownTypes)
            {
                foreach (OperationDescription operationDescription in contractDesc.Operations)
                {
                    if (!operationDescription.IsServerInitiated())
                        operationDescription.KnownTypes.Add(knownType);
                }
            }
 
            if (reflectionInfo.callbackiface != null)
            {
                customAttributes = ServiceReflector.GetCustomAttributes(reflectionInfo.callbackiface, typeof(ServiceKnownTypeAttribute), false);
                knownTypes = GetKnownTypes(customAttributes, reflectionInfo.callbackiface);
                foreach (Type knownType in knownTypes)
                {
                    foreach (OperationDescription operationDescription in contractDesc.Operations)
                    {
                        if (operationDescription.IsServerInitiated())
                            operationDescription.KnownTypes.Add(knownType);
                    }
                }
            }
        }
 
        private IEnumerable<Type> GetKnownTypes(object[] knownTypeAttributes, ICustomAttributeProvider provider)
        {
            if (knownTypeAttributes.Length == 1)
            {
                ServiceKnownTypeAttribute knownTypeAttribute = (ServiceKnownTypeAttribute)knownTypeAttributes[0];
                if (!string.IsNullOrEmpty(knownTypeAttribute.MethodName))
                {
                    Type type = knownTypeAttribute.DeclaringType;
                    if (type == null)
                    {
                        type = provider as Type;
                        if (type == null)
                            type = ((MethodInfo)provider).DeclaringType;
                    }
                    MethodInfo method = type.GetMethod(knownTypeAttribute.MethodName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, null, knownTypesMethodParamType, null);
                    if (method == null)
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxKnownTypeAttributeUnknownMethod3, provider, knownTypeAttribute.MethodName, type.FullName)));
 
                    if (!typeof(IEnumerable<Type>).IsAssignableFrom(method.ReturnType))
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxKnownTypeAttributeReturnType3, provider, knownTypeAttribute.MethodName, type.FullName)));
 
                    return (IEnumerable<Type>)method.Invoke(null, new object[] { provider });
                }
            }
 
            List<Type> knownTypes = new List<Type>();
            for (int i = 0; i < knownTypeAttributes.Length; ++i)
            {
                ServiceKnownTypeAttribute knownTypeAttribute = (ServiceKnownTypeAttribute)knownTypeAttributes[i];
                if (knownTypeAttribute.Type == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxKnownTypeAttributeInvalid1, provider.ToString())));
                knownTypes.Add(knownTypeAttribute.Type);
            }
            return knownTypes;
        }
 
        KeyedByTypeCollection<IOperationBehavior> GetIOperationBehaviorAttributesFromType(OperationDescription opDesc, Type targetIface, Type implType)
        {
            KeyedByTypeCollection<IOperationBehavior> result = new KeyedByTypeCollection<IOperationBehavior>();
            InterfaceMapping ifaceMap = default(InterfaceMapping);
            bool useImplAttrs = false;
            if (implType != null)
            {
                if (targetIface.IsAssignableFrom(implType) && targetIface.IsInterface)
                {
                    ifaceMap = implType.GetInterfaceMap(targetIface);
                    useImplAttrs = true;
                }
                else
                {
                    // implType does not implement any methods from the targetIface, so there is nothing to do
                    return result;
                }
            }
            MethodInfo opMethod = opDesc.OperationMethod;
            ProcessOpMethod(opMethod, true, opDesc, result, ifaceMap, useImplAttrs);
            if (opDesc.SyncMethod != null && opDesc.BeginMethod != null)
            {
                ProcessOpMethod(opDesc.BeginMethod, false, opDesc, result, ifaceMap, useImplAttrs);
            }
            else if (opDesc.SyncMethod != null && opDesc.TaskMethod != null)
            {
                ProcessOpMethod(opDesc.TaskMethod, false, opDesc, result, ifaceMap, useImplAttrs);
            }
            else if (opDesc.TaskMethod != null && opDesc.BeginMethod != null)
            {
                ProcessOpMethod(opDesc.BeginMethod, false, opDesc, result, ifaceMap, useImplAttrs);
            }
            return result;
        }
 
        void ProcessOpMethod(MethodInfo opMethod, bool canHaveBehaviors,
                             OperationDescription opDesc, KeyedByTypeCollection<IOperationBehavior> result,
                             InterfaceMapping ifaceMap, bool useImplAttrs)
        {
            MethodInfo method = null;
            if (useImplAttrs)
            {
                int methodIndex = Array.IndexOf(ifaceMap.InterfaceMethods, opMethod);
                // if opMethod doesn't exist in the interfacemap, it means opMethod was on
                // the "other" interface (not the one implemented by implType)
                if (methodIndex != -1)
                {
                    MethodInfo implMethod = ifaceMap.TargetMethods[methodIndex];
                    // C++ allows you to create abstract classes that have missing interface method
                    // implementations, which shows up as nulls in the interfacemapping
                    if (implMethod != null)
                    {
                        method = implMethod;
                    }
                }
                if (method == null)
                {
                    return;
                }
            }
            else
            {
                method = opMethod;
            }
 
            object[] methodAttributes = ServiceReflector.GetCustomAttributes(method, typeof(IOperationBehavior), false);
            for (int k = 0; k < methodAttributes.Length; k++)
            {
                IOperationBehavior opBehaviorAttr = (IOperationBehavior)methodAttributes[k];
                if (canHaveBehaviors)
                {
                    result.Add(opBehaviorAttr);
                }
                else
                {
                    if (opDesc.SyncMethod != null && opDesc.BeginMethod != null)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new InvalidOperationException(SR.GetString(SR.SyncAsyncMatchConsistency_Attributes6,
                                                                       opDesc.SyncMethod.Name,
                                                                       opDesc.SyncMethod.DeclaringType,
                                                                       opDesc.BeginMethod.Name,
                                                                       opDesc.EndMethod.Name,
                                                                       opDesc.Name,
                                                                       opBehaviorAttr.GetType().FullName)));
                    }
                    else if (opDesc.SyncMethod != null && opDesc.TaskMethod != null)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new InvalidOperationException(SR.GetString(SR.SyncTaskMatchConsistency_Attributes6,
                                                                       opDesc.SyncMethod.Name,
                                                                       opDesc.SyncMethod.DeclaringType,
                                                                       opDesc.TaskMethod.Name,
                                                                       opDesc.Name,
                                                                       opBehaviorAttr.GetType().FullName)));
                    }
                    else if (opDesc.TaskMethod != null && opDesc.BeginMethod != null)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new InvalidOperationException(SR.GetString(SR.TaskAsyncMatchConsistency_Attributes6,
                                                                       opDesc.TaskMethod.Name,
                                                                       opDesc.TaskMethod.DeclaringType,
                                                                       opDesc.BeginMethod.Name,
                                                                       opDesc.EndMethod.Name,
                                                                       opDesc.Name,
                                                                       opBehaviorAttr.GetType().FullName)));
                    }
                    Fx.Assert("Invalid state. No exception for canHaveBehaviors = false");
                }
            }
        }
 
        internal void AddBehaviorsSFx(ServiceEndpoint serviceEndpoint, Type contractType)
        {
            if (serviceEndpoint.Contract.IsDuplex())
            {
                CallbackBehaviorAttribute attr = serviceEndpoint.Behaviors.Find<CallbackBehaviorAttribute>();
                if (attr == null)
                {
                    serviceEndpoint.Behaviors.Insert(0, new CallbackBehaviorAttribute());
                }
            }
        }
 
        internal void AddBehaviorsFromImplementationType(ServiceEndpoint serviceEndpoint, Type implementationType)
        {
            foreach (IEndpointBehavior behaviorAttribute in ServiceReflector.GetCustomAttributes(implementationType, typeof(IEndpointBehavior), false))
            {
                if (behaviorAttribute is CallbackBehaviorAttribute)
                {
                    serviceEndpoint.Behaviors.Insert(0, behaviorAttribute);
                }
                else
                {
                    serviceEndpoint.Behaviors.Add(behaviorAttribute);
                }
            }
            foreach (IContractBehavior behaviorAttribute in ServiceReflector.GetCustomAttributes(implementationType, typeof(IContractBehavior), false))
            {
                serviceEndpoint.Contract.Behaviors.Add(behaviorAttribute);
            }
            Type targetIface = serviceEndpoint.Contract.CallbackContractType;
            for (int i = 0; i < serviceEndpoint.Contract.Operations.Count; i++)
            {
                OperationDescription opDesc = serviceEndpoint.Contract.Operations[i];
                KeyedByTypeCollection<IOperationBehavior> opBehaviors = new KeyedByTypeCollection<IOperationBehavior>();
                // look for IOperationBehaviors on implementation methods in callback class hierarchy
                ApplyServiceInheritance<IOperationBehavior, KeyedByTypeCollection<IOperationBehavior>>(
                    implementationType, opBehaviors,
                    delegate(Type currentType, KeyedByTypeCollection<IOperationBehavior> behaviors)
                    {
                        KeyedByTypeCollection<IOperationBehavior> toAdd =
                            GetIOperationBehaviorAttributesFromType(opDesc, targetIface, currentType);
                        for (int j = 0; j < toAdd.Count; j++)
                        {
                            behaviors.Add(toAdd[j]);
                        }
                    });
                // a bunch of default IOperationBehaviors have already been added, which we may need to replace
                for (int k = 0; k < opBehaviors.Count; k++)
                {
                    IOperationBehavior behavior = opBehaviors[k];
                    Type t = behavior.GetType();
                    if (opDesc.Behaviors.Contains(t))
                    {
                        opDesc.Behaviors.Remove(t);
                    }
                    opDesc.Behaviors.Add(behavior);
                }
            }
        }
 
        internal static int CompareMessagePartDescriptions(MessagePartDescription a, MessagePartDescription b)
        {
            int posCmp = a.SerializationPosition - b.SerializationPosition;
            if (posCmp != 0)
            {
                return posCmp;
            }
 
            int nsCmp = string.Compare(a.Namespace, b.Namespace, StringComparison.Ordinal);
            if (nsCmp != 0)
            {
                return nsCmp;
            }
 
            return string.Compare(a.Name, b.Name, StringComparison.Ordinal);
        }
 
        internal static XmlName GetBodyWrapperResponseName(string operationName)
        {
#if DEBUG
            Fx.Assert(NamingHelper.IsValidNCName(operationName), "operationName value has to be a valid NCName.");
#endif
            return new XmlName(operationName + ResponseSuffix);
        }
 
        internal static XmlName GetBodyWrapperResponseName(XmlName operationName)
        {
            return new XmlName(operationName.EncodedName + ResponseSuffix, true /*isEncoded*/);
        }
 
        void CreateOperationDescriptions(ContractDescription contractDescription,
                                         ContractReflectionInfo reflectionInfo,
                                         Type contractToGetMethodsFrom,
                                         ContractDescription declaringContract,
                                         MessageDirection direction
                                         )
        {
            MessageDirection otherDirection = MessageDirectionHelper.Opposite(direction);
            if (!(declaringContract.ContractType.IsAssignableFrom(contractDescription.ContractType)))
            {
                Fx.Assert("bad contract inheritance");
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                    String.Format(CultureInfo.InvariantCulture, "Bad contract inheritence. Contract {0} does not implement {1}", declaringContract.ContractType.Name, contractDescription.ContractType.Name)
                    ));
            }
 
            foreach (MethodInfo methodInfo in contractToGetMethodsFrom.GetMethods(DefaultBindingFlags))
            {
                if (contractToGetMethodsFrom.IsInterface)
                {
                    object[] attrs = ServiceReflector.GetCustomAttributes(methodInfo, typeof(OperationBehaviorAttribute), false);
                    if (attrs.Length != 0)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                            SR.GetString(SR.SFxOperationBehaviorAttributeOnlyOnServiceClass, methodInfo.Name, contractToGetMethodsFrom.Name)));
                    }
                }
                ServiceReflector.ValidateParameterMetadata(methodInfo);
                OperationDescription operation = CreateOperationDescription(contractDescription, methodInfo, direction, reflectionInfo, declaringContract);
                if (operation != null)
                {
                    contractDescription.Operations.Add(operation);
                }
            }
        }
 
        //Checks whether that the Callback contract provided on a ServiceContract follows rules
        //1. It has to be a interface
        //2. If its a class then it needs to implement MarshallByRefObject
        internal static void EnsureCallbackType(Type callbackType)
        {
            if (callbackType != null && !callbackType.IsInterface && !callbackType.IsMarshalByRef)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SFxInvalidCallbackContractType, callbackType.Name)));
            }
        }
 
        // checks a contract for substitutability (in the Liskov Substitution Principle sense), throws on error
        internal static void EnsureSubcontract(ServiceContractAttribute svcContractAttr, Type contractType)
        {
            Type callbackType = svcContractAttr.CallbackContract;
 
            List<Type> types = ServiceReflector.GetInheritedContractTypes(contractType);
            for (int i = 0; i < types.Count; i++)
            {
                Type inheritedContractType = types[i];
                ServiceContractAttribute inheritedContractAttr = ServiceReflector.GetRequiredSingleAttribute<ServiceContractAttribute>(inheritedContractType);
                // we must be covariant in our callbacks
                if (inheritedContractAttr.CallbackContract != null)
                {
                    if (callbackType == null)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                            SR.GetString(SR.InAContractInheritanceHierarchyIfParentHasCallbackChildMustToo,
                            inheritedContractType.Name, inheritedContractAttr.CallbackContract.Name, contractType.Name)));
                    }
                    if (!inheritedContractAttr.CallbackContract.IsAssignableFrom(callbackType))
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                            SR.GetString(SR.InAContractInheritanceHierarchyTheServiceContract3_2,
                            inheritedContractType.Name, contractType.Name)));
                    }
                }
            }
        }
 
        ContractDescription CreateContractDescription(ServiceContractAttribute contractAttr, Type contractType, Type serviceType, out ContractReflectionInfo reflectionInfo, object serviceImplementation)
        {
            reflectionInfo = new ContractReflectionInfo();
 
            XmlQualifiedName contractName = NamingHelper.GetContractName(contractType, contractAttr.Name, contractAttr.Namespace);
            ContractDescription contractDescription = new ContractDescription(contractName.Name, contractName.Namespace);
            contractDescription.ContractType = contractType;
            if (contractAttr.HasProtectionLevel)
            {
                contractDescription.ProtectionLevel = contractAttr.ProtectionLevel;
            }
 
            Type callbackType = contractAttr.CallbackContract;
 
            EnsureCallbackType(callbackType);
 
            EnsureSubcontract(contractAttr, contractType);
 
            // reflect the methods in contractType and add OperationDescriptions to ContractDescription
            reflectionInfo.iface = contractType;
            reflectionInfo.callbackiface = callbackType;
 
            contractDescription.SessionMode = contractAttr.SessionMode;
            contractDescription.CallbackContractType = callbackType;
            contractDescription.ConfigurationName = contractAttr.ConfigurationName ?? contractType.FullName;
 
            // get inherited operations
            List<Type> types = ServiceReflector.GetInheritedContractTypes(contractType);
            List<Type> inheritedCallbackTypes = new List<Type>();
            for (int i = 0; i < types.Count; i++)
            {
                Type inheritedContractType = types[i];
                ServiceContractAttribute inheritedContractAttr = ServiceReflector.GetRequiredSingleAttribute<ServiceContractAttribute>(inheritedContractType);
                ContractDescription inheritedContractDescription = LoadContractDescriptionHelper(inheritedContractType, serviceType, serviceImplementation);
                foreach (OperationDescription op in inheritedContractDescription.Operations)
                {
                    if (!contractDescription.Operations.Contains(op)) // in a diamond hierarchy, ensure we don't add same op twice from two different parents
                    {
                        // ensure two different parents don't try to add conflicting operations
                        Collection<OperationDescription> existingOps = contractDescription.Operations.FindAll(op.Name);
                        foreach (OperationDescription existingOp in existingOps)
                        {
                            if (existingOp.Messages[0].Direction == op.Messages[0].Direction)
                            {
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                                    SR.GetString(SR.CannotInheritTwoOperationsWithTheSameName3,
                                        op.Name, inheritedContractDescription.Name, existingOp.DeclaringContract.Name)));
                            }
                        }
                        contractDescription.Operations.Add(op);
                    }
                }
                if (inheritedContractDescription.CallbackContractType != null)
                {
                    inheritedCallbackTypes.Add(inheritedContractDescription.CallbackContractType);
                }
            }
 
            // this contract 
            CreateOperationDescriptions(contractDescription, reflectionInfo, contractType, contractDescription, MessageDirection.Input);
            // CallbackContract 
            if (callbackType != null && !inheritedCallbackTypes.Contains(callbackType))
            {
                CreateOperationDescriptions(contractDescription, reflectionInfo, callbackType, contractDescription, MessageDirection.Output);
            }
 
            return contractDescription;
        }
 
        internal static Attribute GetFormattingAttribute(ICustomAttributeProvider attrProvider, Attribute defaultFormatAttribute)
        {
            if (attrProvider != null)
            {
                if (attrProvider.IsDefined(typeof(XmlSerializerFormatAttribute), false))
                {
                    return ServiceReflector.GetSingleAttribute<XmlSerializerFormatAttribute>(attrProvider, formatterAttributes);
                }
                if (attrProvider.IsDefined(typeof(DataContractFormatAttribute), false))
                {
                    return ServiceReflector.GetSingleAttribute<DataContractFormatAttribute>(attrProvider, formatterAttributes);
                }
            }
            return defaultFormatAttribute;
        }
 
        //Sync and Async should follow the rules:
        //    1. Parameter match
        //    2. Async cannot have behaviors (verification happens later in ProcessOpMethod - behaviors haven't yet been loaded here)
        //    3. Async cannot have known types
        //    4. Async cannot have known faults
        //    5. Sync and Async have to match on OneWay status
        //    6. Sync and Async have to match Action and ReplyAction
        void VerifyConsistency(OperationConsistencyVerifier verifier)
        {
            verifier.VerifyParameterLength();
            verifier.VerifyParameterType();
            verifier.VerifyOutParameterType();
            verifier.VerifyReturnType();
            verifier.VerifyFaultContractAttribute();
            verifier.VerifyKnownTypeAttribute();
            verifier.VerifyIsOneWayStatus();
            verifier.VerifyActionAndReplyAction();
        }
 
        // "direction" is the "direction of the interface" (from the perspective of the server, as usual):
        //    proxy interface on client: MessageDirection.Input
        //    callback interface on client: MessageDirection.Output
        //    service interface (or class) on server: MessageDirection.Input
        //    callback interface on server: MessageDirection.Output
        OperationDescription CreateOperationDescription(ContractDescription contractDescription, MethodInfo methodInfo, MessageDirection direction,
                                                        ContractReflectionInfo reflectionInfo, ContractDescription declaringContract)
        {
            OperationContractAttribute opAttr = ServiceReflector.GetOperationContractAttribute(methodInfo);
            if (opAttr == null)
            {
                return null;
            }
 
            if (ServiceReflector.HasEndMethodShape(methodInfo))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                    SR.GetString(SR.EndMethodsCannotBeDecoratedWithOperationContractAttribute,
                                 methodInfo.Name, reflectionInfo.iface)));
            }
 
            Type taskTResult;
            bool isTask = ServiceReflector.IsTask(methodInfo, out taskTResult);
            bool isAsync = isTask ? false : ServiceReflector.IsBegin(opAttr, methodInfo);
 
            XmlName operationName = NamingHelper.GetOperationName(ServiceReflector.GetLogicalName(methodInfo, isAsync, isTask), opAttr.Name);
 
            opAttr.EnsureInvariants(methodInfo, operationName.EncodedName);
 
            Collection<OperationDescription> operations = contractDescription.Operations.FindAll(operationName.EncodedName);
            for (int i = 0; i < operations.Count; i++)
            {
                OperationDescription existingOp = operations[i];
                if (existingOp.Messages[0].Direction == direction)
                {
                    // if we have already seen a task-based method with the same name, we need to throw.
                    if (existingOp.TaskMethod != null && isTask)
                    {
                        string method1Name = existingOp.OperationMethod.Name;
                        string method2Name = methodInfo.Name;
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.CannotHaveTwoOperationsWithTheSameName3, method1Name, method2Name, reflectionInfo.iface)));
                    }
                    if (isAsync && (existingOp.BeginMethod != null))
                    {
                        string method1Name = existingOp.BeginMethod.Name;
                        string method2Name = methodInfo.Name;
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.CannotHaveTwoOperationsWithTheSameName3, method1Name, method2Name, reflectionInfo.iface)));
                    }
                    if (!isAsync && !isTask && (existingOp.SyncMethod != null))
                    {
                        string method1Name = existingOp.SyncMethod.Name;
                        string method2Name = methodInfo.Name;
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.CannotHaveTwoOperationsWithTheSameName3, method1Name, method2Name, reflectionInfo.iface)));
                    }
 
                    contractDescription.Operations.Remove(existingOp);
                    OperationDescription newOp = CreateOperationDescription(contractDescription,
                                                                            methodInfo,
                                                                            direction,
                                                                            reflectionInfo,
                                                                            declaringContract);
 
                    newOp.HasNoDisposableParameters = ServiceReflector.HasNoDisposableParameters(methodInfo);
 
                    if (isTask)
                    {
                        existingOp.TaskMethod = newOp.TaskMethod;
                        existingOp.TaskTResult = newOp.TaskTResult;
                        if (existingOp.SyncMethod != null)
                        {
                            // Task vs. Sync 
                            VerifyConsistency(new SyncTaskOperationConsistencyVerifier(existingOp, newOp));
                        }
                        else
                        {
                            // Task vs. Async
                            VerifyConsistency(new TaskAsyncOperationConsistencyVerifier(newOp, existingOp));
                        }
                        return existingOp;
                    }
                    else if (isAsync)
                    {
                        existingOp.BeginMethod = newOp.BeginMethod;
                        existingOp.EndMethod = newOp.EndMethod;
                        if (existingOp.SyncMethod != null)
                        {
                            // Async vs. Sync
                            VerifyConsistency(new SyncAsyncOperationConsistencyVerifier(existingOp, newOp));
                        }
                        else
                        {
                            // Async vs. Task
                            VerifyConsistency(new TaskAsyncOperationConsistencyVerifier(existingOp, newOp));
                        }
                        return existingOp;
                    }
                    else
                    {
                        newOp.BeginMethod = existingOp.BeginMethod;
                        newOp.EndMethod = existingOp.EndMethod;
                        newOp.TaskMethod = existingOp.TaskMethod;
                        newOp.TaskTResult = existingOp.TaskTResult;
                        if (existingOp.TaskMethod != null)
                        {
                            // Sync vs. Task
                            VerifyConsistency(new SyncTaskOperationConsistencyVerifier(newOp, existingOp));
                        }
                        else
                        {
                            // Sync vs. Async
                            VerifyConsistency(new SyncAsyncOperationConsistencyVerifier(newOp, existingOp));
                        }
                        return newOp;
                    }
                }
            }
 
            OperationDescription operationDescription = new OperationDescription(operationName.EncodedName, declaringContract);
            operationDescription.IsInitiating = opAttr.IsInitiating;
            operationDescription.IsTerminating = opAttr.IsTerminating;
            operationDescription.IsSessionOpenNotificationEnabled = opAttr.IsSessionOpenNotificationEnabled;
 
            operationDescription.HasNoDisposableParameters = ServiceReflector.HasNoDisposableParameters(methodInfo);
 
            if (opAttr.HasProtectionLevel)
            {
                operationDescription.ProtectionLevel = opAttr.ProtectionLevel;
            }
 
            XmlQualifiedName contractQname = new XmlQualifiedName(declaringContract.Name, declaringContract.Namespace);
 
            object[] methodAttributes = ServiceReflector.GetCustomAttributes(methodInfo, typeof(FaultContractAttribute), false);
 
            if (opAttr.IsOneWay && methodAttributes.Length > 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.OneWayAndFaultsIncompatible2, methodInfo.DeclaringType.FullName, operationName.EncodedName)));
            }
 
            for (int i = 0; i < methodAttributes.Length; i++)
            {
                FaultContractAttribute knownFault = (FaultContractAttribute)methodAttributes[i];
                FaultDescription faultDescription = CreateFaultDescription(knownFault, contractQname, declaringContract.Namespace, operationDescription.XmlName);
                CheckDuplicateFaultContract(operationDescription.Faults, faultDescription, operationName.EncodedName);
                operationDescription.Faults.Add(faultDescription);
            }
 
            methodAttributes = ServiceReflector.GetCustomAttributes(methodInfo, typeof(ServiceKnownTypeAttribute), false);
            IEnumerable<Type> knownTypes = GetKnownTypes(methodAttributes, methodInfo);
            foreach (Type knownType in knownTypes)
                operationDescription.KnownTypes.Add(knownType);
 
            MessageDirection requestDirection = direction;
            MessageDirection responseDirection = MessageDirectionHelper.Opposite(direction);
 
            string requestAction = NamingHelper.GetMessageAction(contractQname,
                                                                   operationDescription.CodeName,
                                                                   opAttr.Action,
                                                                   false);
 
            string responseAction = NamingHelper.GetMessageAction(contractQname,
                                                                  operationDescription.CodeName,
                                                                  opAttr.ReplyAction,
                                                                  true);
            XmlName wrapperName = operationName;
            XmlName wrapperResponseName = GetBodyWrapperResponseName(operationName);
            string wrapperNamespace = declaringContract.Namespace;
 
            MessageDescription requestDescription = CreateMessageDescription(methodInfo,
                                                           isAsync,
                                                           isTask,
                                                           null,
                                                           null,
                                                           contractDescription.Namespace,
                                                           requestAction,
                                                           wrapperName,
                                                           wrapperNamespace,
                                                           requestDirection);
            MessageDescription responseDescription = null;
            operationDescription.Messages.Add(requestDescription);
            MethodInfo outputMethod = methodInfo;
            if (isTask)
            {
                operationDescription.TaskMethod = methodInfo;
                operationDescription.TaskTResult = taskTResult;
            }
            else if (!isAsync)
            {
                operationDescription.SyncMethod = methodInfo;
            }
            else
            {
                outputMethod = ServiceReflector.GetEndMethod(methodInfo);
                operationDescription.EndMethod = outputMethod;
                operationDescription.BeginMethod = methodInfo;
            }
 
            if (!opAttr.IsOneWay)
            {
                XmlName returnValueName = GetReturnValueName(operationName);
                responseDescription = CreateMessageDescription(outputMethod,
                                                                isAsync,
                                                                isTask,
                                                                taskTResult,
                                                                returnValueName,
                                                                contractDescription.Namespace,
                                                                responseAction,
                                                                wrapperResponseName,
                                                                wrapperNamespace,
                                                                responseDirection);
                operationDescription.Messages.Add(responseDescription);
            }
            else
            {
                if ((!isTask && outputMethod.ReturnType != ServiceReflector.VoidType) || (isTask && taskTResult != ServiceReflector.VoidType) ||
                    ServiceReflector.HasOutputParameters(outputMethod, isAsync))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ServiceOperationsMarkedWithIsOneWayTrueMust0)));
                }
 
                if (opAttr.ReplyAction != null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.OneWayOperationShouldNotSpecifyAReplyAction1, operationName)));
                }
            }
 
            if (!opAttr.IsOneWay)
            {
                if (responseDescription.IsVoid &&
                    (requestDescription.IsUntypedMessage || requestDescription.IsTypedMessage))
                {
                    responseDescription.Body.WrapperName = responseDescription.Body.WrapperNamespace = null;
                }
                else if (requestDescription.IsVoid &&
                    (responseDescription.IsUntypedMessage || responseDescription.IsTypedMessage))
                {
                    requestDescription.Body.WrapperName = requestDescription.Body.WrapperNamespace = null;
                }
            }
            return operationDescription;
        }
 
        private void CheckDuplicateFaultContract(FaultDescriptionCollection faultDescriptionCollection, FaultDescription fault, string operationName)
        {
            foreach (FaultDescription existingFault in faultDescriptionCollection)
            {
                if (XmlName.IsNullOrEmpty(existingFault.ElementName) && XmlName.IsNullOrEmpty(fault.ElementName) && existingFault.DetailType == fault.DetailType)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                        SR.GetString(SR.SFxFaultContractDuplicateDetailType, operationName, fault.DetailType)));
                if (!XmlName.IsNullOrEmpty(existingFault.ElementName) && !XmlName.IsNullOrEmpty(fault.ElementName) && existingFault.ElementName == fault.ElementName && existingFault.Namespace == fault.Namespace)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                        SR.GetString(SR.SFxFaultContractDuplicateElement, operationName, fault.ElementName, fault.Namespace)));
            }
        }
 
        FaultDescription CreateFaultDescription(FaultContractAttribute attr,
                                                XmlQualifiedName contractName,
                                                string contractNamespace,
                                                XmlName operationName)
        {
            XmlName faultName = new XmlName(attr.Name ?? NamingHelper.TypeName(attr.DetailType) + FaultSuffix);
            FaultDescription fault = new FaultDescription(NamingHelper.GetMessageAction(contractName, operationName.DecodedName + faultName.DecodedName, attr.Action, false/*isResponse*/));
            if (attr.Name != null)
                fault.SetNameAndElement(faultName);
            else
                fault.SetNameOnly(faultName);
            fault.Namespace = attr.Namespace ?? contractNamespace;
            fault.DetailType = attr.DetailType;
            if (attr.HasProtectionLevel)
            {
                fault.ProtectionLevel = attr.ProtectionLevel;
            }
            return fault;
        }
 
        MessageDescription CreateMessageDescription(MethodInfo methodInfo,
                                                           bool isAsync,
                                                           bool isTask,
                                                           Type taskTResult,
                                                           XmlName returnValueName,
                                                           string defaultNS,
                                                           string action,
                                                           XmlName wrapperName,
                                                           string wrapperNamespace,
                                                           MessageDirection direction)
        {
            string methodName = methodInfo.Name;
            MessageDescription messageDescription;
            if (returnValueName == null)
            {
                ParameterInfo[] parameters = ServiceReflector.GetInputParameters(methodInfo, isAsync);
                if (parameters.Length == 1 && parameters[0].ParameterType.IsDefined(typeof(MessageContractAttribute), false))
                {
                    messageDescription = CreateTypedMessageDescription(parameters[0].ParameterType,
                                                                null,
                                                                null,
                                                                defaultNS,
                                                                action,
                                                                direction);
                }
                else
                {
                    messageDescription = CreateParameterMessageDescription(parameters,
                                                             null,
                                                             null,
                                                             null,
                                                             methodName,
                                                             defaultNS,
                                                             action,
                                                             wrapperName,
                                                             wrapperNamespace,
                                                             direction);
                }
            }
            else
            {
                ParameterInfo[] parameters = ServiceReflector.GetOutputParameters(methodInfo, isAsync);
                Type responseType = isTask ? taskTResult : methodInfo.ReturnType;
                if (responseType.IsDefined(typeof(MessageContractAttribute), false) && parameters.Length == 0)
                {
                    messageDescription = CreateTypedMessageDescription(responseType,
                                                         methodInfo.ReturnTypeCustomAttributes,
                                                         returnValueName,
                                                         defaultNS,
                                                         action,
                                                         direction);
                }
                else
                {
                    messageDescription = CreateParameterMessageDescription(parameters,
                                                         responseType,
                                                         methodInfo.ReturnTypeCustomAttributes,
                                                         returnValueName,
                                                         methodName,
                                                         defaultNS,
                                                         action,
                                                         wrapperName,
                                                         wrapperNamespace,
                                                         direction);
                }
            }
 
            bool hasUnknownHeaders = false;
            for (int i = 0; i < messageDescription.Headers.Count; i++)
            {
                MessageHeaderDescription header = messageDescription.Headers[i];
                if (header.IsUnknownHeaderCollection)
                {
                    if (hasUnknownHeaders)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxMultipleUnknownHeaders, methodInfo, methodInfo.DeclaringType)));
                    }
                    else
                    {
                        hasUnknownHeaders = true;
                    }
                }
            }
            return messageDescription;
        }
 
        MessageDescription CreateParameterMessageDescription(ParameterInfo[] parameters,
                                                  Type returnType,
                                                  ICustomAttributeProvider returnAttrProvider,
                                                  XmlName returnValueName,
                                                  string methodName,
                                                  string defaultNS,
                                                  string action,
                                                  XmlName wrapperName,
                                                  string wrapperNamespace,
                                                  MessageDirection direction)
        {
            foreach (ParameterInfo param in parameters)
            {
                if (GetParameterType(param).IsDefined(typeof(MessageContractAttribute), false/*inherit*/))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInvalidMessageContractSignature, methodName)));
                }
            }
            if (returnType != null && returnType.IsDefined(typeof(MessageContractAttribute), false/*inherit*/))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInvalidMessageContractSignature, methodName)));
            }
 
            MessageDescription messageDescription = new MessageDescription(action, direction);
            MessagePartDescriptionCollection partDescriptionCollection = messageDescription.Body.Parts;
            for (int index = 0; index < parameters.Length; index++)
            {
                MessagePartDescription partDescription = CreateParameterPartDescription(new XmlName(parameters[index].Name), defaultNS, index, parameters[index], GetParameterType(parameters[index]));
                if (partDescriptionCollection.Contains(new XmlQualifiedName(partDescription.Name, partDescription.Namespace)))
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidMessageContractException(SR.GetString(SR.SFxDuplicateMessageParts, partDescription.Name, partDescription.Namespace)));
                messageDescription.Body.Parts.Add(partDescription);
            }
 
            if (returnType != null)
            {
                messageDescription.Body.ReturnValue = CreateParameterPartDescription(returnValueName, defaultNS, 0, returnAttrProvider, returnType);
            }
            if (messageDescription.IsUntypedMessage)
            {
                messageDescription.Body.WrapperName = null;
                messageDescription.Body.WrapperNamespace = null;
            }
            else
            {
                messageDescription.Body.WrapperName = wrapperName.EncodedName;
                messageDescription.Body.WrapperNamespace = wrapperNamespace;
            }
 
            return messageDescription;
        }
 
        private static MessagePartDescription CreateParameterPartDescription(XmlName defaultName, string defaultNS, int index, ICustomAttributeProvider attrProvider, Type type)
        {
            MessagePartDescription parameterPart;
            MessageParameterAttribute paramAttr = ServiceReflector.GetSingleAttribute<MessageParameterAttribute>(attrProvider);
 
            XmlName name = paramAttr == null || !paramAttr.IsNameSetExplicit ? defaultName : new XmlName(paramAttr.Name);
            parameterPart = new MessagePartDescription(name.EncodedName, defaultNS);
            parameterPart.Type = type;
            parameterPart.Index = index;
            parameterPart.AdditionalAttributesProvider = attrProvider;
            return parameterPart;
        }
 
        [SuppressMessage(FxCop.Category.Usage, "CA2301:EmbeddableTypesInContainersRule", MessageId = "messages", Justification = "No need to support type equivalence here.")]
        internal MessageDescription CreateTypedMessageDescription(Type typedMessageType,
                                                  ICustomAttributeProvider returnAttrProvider,
                                                  XmlName returnValueName,
                                                  string defaultNS,
                                                  string action,
                                                  MessageDirection direction)
        {
 
 
            MessageDescription messageDescription;
            bool messageItemsInitialized = false;
            MessageDescriptionItems messageItems;
            MessageContractAttribute messageContractAttribute = ServiceReflector.GetSingleAttribute<MessageContractAttribute>(typedMessageType);
            if (messages.TryGetValue(typedMessageType, out messageItems))
            {
                messageDescription = new MessageDescription(action, direction, messageItems);
                messageItemsInitialized = true;
            }
            else
                messageDescription = new MessageDescription(action, direction, null);
            messageDescription.MessageType = typedMessageType;
            messageDescription.MessageName = new XmlName(NamingHelper.TypeName(typedMessageType));
            if (messageContractAttribute.IsWrapped)
            {
                messageDescription.Body.WrapperName = GetWrapperName(messageContractAttribute.WrapperName, messageDescription.MessageName).EncodedName;
                messageDescription.Body.WrapperNamespace = messageContractAttribute.WrapperNamespace ?? defaultNS;
            }
            List<MemberInfo> contractMembers = new List<MemberInfo>();
 
            for (Type baseType = typedMessageType; baseType != null && baseType != typeof(object) && baseType != typeof(ValueType); baseType = baseType.BaseType)
            {
                if (!baseType.IsDefined(typeof(MessageContractAttribute), false/*inherit*/))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxMessageContractBaseTypeNotValid, baseType, typedMessageType)));
                }
                if (!messageDescription.HasProtectionLevel)
                {
                    MessageContractAttribute mca = ServiceReflector.GetRequiredSingleAttribute<MessageContractAttribute>(baseType);
                    if (mca.HasProtectionLevel)
                    {
                        messageDescription.ProtectionLevel = mca.ProtectionLevel;
                    }
                }
 
                if (messageItemsInitialized)
                    continue;
                foreach (MemberInfo memberInfo in baseType.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
                {
                    if (memberInfo.MemberType != MemberTypes.Field &&
                        memberInfo.MemberType != MemberTypes.Property)
                    {
                        continue;
                    }
                    PropertyInfo property = memberInfo as PropertyInfo;
                    if (property != null)
                    {
                        MethodInfo getMethod = property.GetGetMethod(true);
                        if (getMethod != null && IsMethodOverriding(getMethod))
                        {
                            continue;
                        }
                        MethodInfo setMethod = property.GetSetMethod(true);
                        if (setMethod != null && IsMethodOverriding(setMethod))
                        {
                            continue;
                        }
                    }
 
                    if (memberInfo.IsDefined(typeof(MessageBodyMemberAttribute), false) ||
                        memberInfo.IsDefined(typeof(MessageHeaderAttribute), false) ||
                        memberInfo.IsDefined(typeof(MessageHeaderArrayAttribute), false) ||
                        memberInfo.IsDefined(typeof(MessagePropertyAttribute), false)
                        )
                    {
                        contractMembers.Add(memberInfo);
                    }
                }
            }
 
            if (messageItemsInitialized)
                return messageDescription;
 
            List<MessagePartDescription> bodyPartDescriptionList = new List<MessagePartDescription>();
            List<MessageHeaderDescription> headerPartDescriptionList = new List<MessageHeaderDescription>();
            for (int i = 0; i < contractMembers.Count; i++)
            {
                MemberInfo memberInfo = contractMembers[i];
 
                Type memberType;
                if (memberInfo.MemberType == MemberTypes.Property)
                {
                    memberType = ((PropertyInfo)memberInfo).PropertyType;
                }
                else
                {
                    memberType = ((FieldInfo)memberInfo).FieldType;
                }
 
                if (memberInfo.IsDefined(typeof(MessageHeaderArrayAttribute), false)
                    || memberInfo.IsDefined(typeof(MessageHeaderAttribute), false))
                {
                    headerPartDescriptionList.Add(CreateMessageHeaderDescription(memberType,
                                                                              memberInfo,
                                                                              new XmlName(memberInfo.Name),
                                                                              defaultNS,
                                                                              i,
                                                                              -1));
                }
                else if (memberInfo.IsDefined(typeof(MessagePropertyAttribute), false))
                {
                    messageDescription.Properties.Add(CreateMessagePropertyDescription(memberInfo,
                                                                              new XmlName(memberInfo.Name),
                                                                              i));
                }
                else
                {
                    bodyPartDescriptionList.Add(CreateMessagePartDescription(memberType,
                                                                         memberInfo,
                                                                         new XmlName(memberInfo.Name),
                                                                         defaultNS,
                                                                         i,
                                                                         -1));
                }
            }
 
            if (returnAttrProvider != null)
            {
                messageDescription.Body.ReturnValue = CreateMessagePartDescription(typeof(void),
                                                                  returnAttrProvider,
                                                                  returnValueName,
                                                                  defaultNS,
                                                                  0,
                                                                  0);
            }
 
            AddSortedParts<MessagePartDescription>(bodyPartDescriptionList, messageDescription.Body.Parts);
            AddSortedParts<MessageHeaderDescription>(headerPartDescriptionList, messageDescription.Headers);
            messages.Add(typedMessageType, messageDescription.Items);
 
            return messageDescription;
        }
 
        static bool IsMethodOverriding(MethodInfo method)
        {
            return method.IsVirtual && ((method.Attributes & MethodAttributes.NewSlot) == 0);
        }
 
 
 
 
        MessagePartDescription CreateMessagePartDescription(Type bodyType,
                                                         ICustomAttributeProvider attrProvider,
                                                         XmlName defaultName,
                                                         string defaultNS,
                                                         int parameterIndex,
                                                         int serializationIndex)
        {
            MessagePartDescription partDescription = null;
            MessageBodyMemberAttribute bodyAttr = ServiceReflector.GetSingleAttribute<MessageBodyMemberAttribute>(attrProvider, messageContractMemberAttributes);
 
            if (bodyAttr == null)
            {
                partDescription = new MessagePartDescription(defaultName.EncodedName, defaultNS);
                partDescription.SerializationPosition = serializationIndex;
            }
            else
            {
                XmlName partName = bodyAttr.IsNameSetExplicit ? new XmlName(bodyAttr.Name) : defaultName;
                string partNs = bodyAttr.IsNamespaceSetExplicit ? bodyAttr.Namespace : defaultNS;
                partDescription = new MessagePartDescription(partName.EncodedName, partNs);
                partDescription.SerializationPosition = bodyAttr.Order < 0 ? serializationIndex : bodyAttr.Order;
                if (bodyAttr.HasProtectionLevel)
                {
                    partDescription.ProtectionLevel = bodyAttr.ProtectionLevel;
                }
            }
 
            if (attrProvider is MemberInfo)
            {
                partDescription.MemberInfo = (MemberInfo)attrProvider;
            }
            partDescription.Type = bodyType;
            partDescription.Index = parameterIndex;
            return partDescription;
        }
 
        MessageHeaderDescription CreateMessageHeaderDescription(Type headerParameterType,
                                                                    ICustomAttributeProvider attrProvider,
                                                                    XmlName defaultName,
                                                                    string defaultNS,
                                                                    int parameterIndex,
                                                                    int serializationPosition)
        {
            MessageHeaderDescription headerDescription = null;
            MessageHeaderAttribute headerAttr = ServiceReflector.GetRequiredSingleAttribute<MessageHeaderAttribute>(attrProvider, messageContractMemberAttributes);
            XmlName headerName = headerAttr.IsNameSetExplicit ? new XmlName(headerAttr.Name) : defaultName;
            string headerNs = headerAttr.IsNamespaceSetExplicit ? headerAttr.Namespace : defaultNS;
            headerDescription = new MessageHeaderDescription(headerName.EncodedName, headerNs);
            headerDescription.UniquePartName = defaultName.EncodedName;
 
            if (headerAttr is MessageHeaderArrayAttribute)
            {
                if (!headerParameterType.IsArray || headerParameterType.GetArrayRank() != 1)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInvalidMessageHeaderArrayType, defaultName)));
                }
                headerDescription.Multiple = true;
                headerParameterType = headerParameterType.GetElementType();
            }
            headerDescription.Type = TypedHeaderManager.GetHeaderType(headerParameterType);
            headerDescription.TypedHeader = (headerParameterType != headerDescription.Type);
            if (headerDescription.TypedHeader)
            {
                if (headerAttr.IsMustUnderstandSet || headerAttr.IsRelaySet || headerAttr.Actor != null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxStaticMessageHeaderPropertiesNotAllowed, defaultName)));
                }
            }
            else
            {
                headerDescription.Actor = headerAttr.Actor;
                headerDescription.MustUnderstand = headerAttr.MustUnderstand;
                headerDescription.Relay = headerAttr.Relay;
            }
            headerDescription.SerializationPosition = serializationPosition;
            if (headerAttr.HasProtectionLevel)
            {
                headerDescription.ProtectionLevel = headerAttr.ProtectionLevel;
            }
            if (attrProvider is MemberInfo)
            {
                headerDescription.MemberInfo = (MemberInfo)attrProvider;
            }
 
            headerDescription.Index = parameterIndex;
            return headerDescription;
        }
 
        MessagePropertyDescription CreateMessagePropertyDescription(ICustomAttributeProvider attrProvider,
                                                            XmlName defaultName,
                                                            int parameterIndex)
        {
            MessagePropertyAttribute attr = ServiceReflector.GetSingleAttribute<MessagePropertyAttribute>(attrProvider, messageContractMemberAttributes);
            XmlName propertyName = attr.IsNameSetExplicit ? new XmlName(attr.Name) : defaultName;
            MessagePropertyDescription propertyDescription = new MessagePropertyDescription(propertyName.EncodedName);
            propertyDescription.Index = parameterIndex;
 
            if (attrProvider is MemberInfo)
            {
                propertyDescription.MemberInfo = (MemberInfo)attrProvider;
            }
 
            return propertyDescription;
        }
 
        internal static XmlName GetReturnValueName(XmlName methodName)
        {
            return new XmlName(methodName.EncodedName + ReturnSuffix, true);
        }
 
        internal static XmlName GetReturnValueName(string methodName)
        {
            return new XmlName(methodName + ReturnSuffix);
        }
 
        internal static Type GetParameterType(ParameterInfo parameterInfo)
        {
            Type parameterType = parameterInfo.ParameterType;
            if (parameterType.IsByRef)
            {
                return parameterType.GetElementType();
            }
            else
            {
                return parameterType;
            }
        }
 
        internal static XmlName GetWrapperName(string wrapperName, XmlName defaultName)
        {
            if (string.IsNullOrEmpty(wrapperName))
                return defaultName;
            return new XmlName(wrapperName);
        }
 
        void AddSortedParts<T>(List<T> partDescriptionList, KeyedCollection<XmlQualifiedName, T> partDescriptionCollection)
            where T : MessagePartDescription
        {
            MessagePartDescription[] partDescriptions = partDescriptionList.ToArray();
            if (partDescriptions.Length > 1)
            {
                Array.Sort<MessagePartDescription>(partDescriptions, CompareMessagePartDescriptions);
            }
            foreach (T partDescription in partDescriptions)
            {
                if (partDescriptionCollection.Contains(new XmlQualifiedName(partDescription.Name, partDescription.Namespace)))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidMessageContractException(SR.GetString(SR.SFxDuplicateMessageParts, partDescription.Name, partDescription.Namespace)));
                }
                partDescriptionCollection.Add(partDescription);
            }
        }
 
        private abstract class OperationConsistencyVerifier
        {
            public virtual void VerifyParameterLength() { }
            public virtual void VerifyParameterType() { }
            public virtual void VerifyOutParameterType() { }
            public virtual void VerifyReturnType() { }
            public virtual void VerifyFaultContractAttribute() { }
            public virtual void VerifyKnownTypeAttribute() { }
            public virtual void VerifyIsOneWayStatus() { }
            public virtual void VerifyActionAndReplyAction() { }
        }
 
        private class SyncAsyncOperationConsistencyVerifier : OperationConsistencyVerifier
        {
            OperationDescription syncOperation;
            OperationDescription asyncOperation;
            ParameterInfo[] syncInputs;
            ParameterInfo[] asyncInputs;
            ParameterInfo[] syncOutputs;
            ParameterInfo[] asyncOutputs;
 
            public SyncAsyncOperationConsistencyVerifier(OperationDescription syncOperation, OperationDescription asyncOperation)
            {
                this.syncOperation = syncOperation;
                this.asyncOperation = asyncOperation;
                this.syncInputs = ServiceReflector.GetInputParameters(this.syncOperation.SyncMethod, false);
                this.asyncInputs = ServiceReflector.GetInputParameters(this.asyncOperation.BeginMethod, true);
                this.syncOutputs = ServiceReflector.GetOutputParameters(this.syncOperation.SyncMethod, false);
                this.asyncOutputs = ServiceReflector.GetOutputParameters(this.asyncOperation.EndMethod, true);
            }
 
            public override void VerifyParameterLength()
            {
                if (this.syncInputs.Length != this.asyncInputs.Length || this.syncOutputs.Length != this.asyncOutputs.Length)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new InvalidOperationException(SR.GetString(SR.SyncAsyncMatchConsistency_Parameters5,
                                                                   this.syncOperation.SyncMethod.Name,
                                                                   this.syncOperation.SyncMethod.DeclaringType,
                                                                   this.asyncOperation.BeginMethod.Name,
                                                                   this.asyncOperation.EndMethod.Name,
                                                                   this.syncOperation.Name)));
                }
            }
 
            public override void VerifyParameterType()
            {
                for (int i = 0; i < this.syncInputs.Length; i++)
                {
                    if (this.syncInputs[i].ParameterType != this.asyncInputs[i].ParameterType)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new InvalidOperationException(SR.GetString(SR.SyncAsyncMatchConsistency_Parameters5,
                                                                       this.syncOperation.SyncMethod.Name,
                                                                       this.syncOperation.SyncMethod.DeclaringType,
                                                                       this.asyncOperation.BeginMethod.Name,
                                                                       this.asyncOperation.EndMethod.Name,
                                                                       this.syncOperation.Name)));
                    }
                }
            }
 
            public override void VerifyOutParameterType()
            {
                for (int i = 0; i < this.syncOutputs.Length; i++)
                {
                    if (this.syncOutputs[i].ParameterType != this.asyncOutputs[i].ParameterType)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new InvalidOperationException(SR.GetString(SR.SyncAsyncMatchConsistency_Parameters5,
                                                                       this.syncOperation.SyncMethod.Name,
                                                                       this.syncOperation.SyncMethod.DeclaringType,
                                                                       this.asyncOperation.BeginMethod.Name,
                                                                       this.asyncOperation.EndMethod.Name,
                                                                       this.syncOperation.Name)));
                    }
                }
            }
 
            public override void VerifyReturnType()
            {
                if (this.syncOperation.SyncMethod.ReturnType != this.syncOperation.EndMethod.ReturnType)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new InvalidOperationException(SR.GetString(SR.SyncAsyncMatchConsistency_ReturnType5,
                                                                   this.syncOperation.SyncMethod.Name,
                                                                   this.syncOperation.SyncMethod.DeclaringType,
                                                                   this.asyncOperation.BeginMethod.Name,
                                                                   this.asyncOperation.EndMethod.Name,
                                                                   this.syncOperation.Name)));
                }
            }
 
            public override void VerifyFaultContractAttribute()
            {
                if (this.asyncOperation.Faults.Count != 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new InvalidOperationException(SR.GetString(SR.SyncAsyncMatchConsistency_Attributes6,
                                                                   this.syncOperation.SyncMethod.Name,
                                                                   this.syncOperation.SyncMethod.DeclaringType,
                                                                   this.asyncOperation.BeginMethod.Name,
                                                                   this.asyncOperation.EndMethod.Name,
                                                                   this.syncOperation.Name,
                                                                   typeof(FaultContractAttribute).Name)));
 
                }
            }
 
            public override void VerifyKnownTypeAttribute()
            {
                if (this.asyncOperation.KnownTypes.Count != 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new InvalidOperationException(SR.GetString(SR.SyncAsyncMatchConsistency_Attributes6,
                                                                   this.syncOperation.SyncMethod.Name,
                                                                   this.syncOperation.SyncMethod.DeclaringType,
                                                                   this.asyncOperation.BeginMethod.Name,
                                                                   this.asyncOperation.EndMethod.Name,
                                                                   this.syncOperation.Name,
                                                                   typeof(ServiceKnownTypeAttribute).Name)));
                }
            }
 
            public override void VerifyIsOneWayStatus()
            {
                if (this.syncOperation.Messages.Count != this.asyncOperation.Messages.Count)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new InvalidOperationException(SR.GetString(SR.SyncAsyncMatchConsistency_Property6,
                                                                       this.syncOperation.SyncMethod.Name,
                                                                       this.syncOperation.SyncMethod.DeclaringType,
                                                                       this.asyncOperation.BeginMethod.Name,
                                                                       this.asyncOperation.EndMethod.Name,
                                                                       this.syncOperation.Name,
                                                                       "IsOneWay")));
                }
            }
 
            public override void VerifyActionAndReplyAction()
            {
                for (int index = 0; index < this.syncOperation.Messages.Count; ++index)
                {
                    if (this.syncOperation.Messages[index].Action != this.asyncOperation.Messages[index].Action)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new InvalidOperationException(SR.GetString(SR.SyncAsyncMatchConsistency_Property6,
                                                                       this.syncOperation.SyncMethod.Name,
                                                                       this.syncOperation.SyncMethod.DeclaringType,
                                                                       this.asyncOperation.BeginMethod.Name,
                                                                       this.asyncOperation.EndMethod.Name,
                                                                       this.syncOperation.Name,
                                                                       index == 0 ? "Action" : "ReplyAction")));
                    }
                }
            }
        }
 
        private class SyncTaskOperationConsistencyVerifier : OperationConsistencyVerifier
        {
            OperationDescription syncOperation;
            OperationDescription taskOperation;
            ParameterInfo[] syncInputs;
            ParameterInfo[] taskInputs;
 
            public SyncTaskOperationConsistencyVerifier(OperationDescription syncOperation, OperationDescription taskOperation)
            {
                this.syncOperation = syncOperation;
                this.taskOperation = taskOperation;
                this.syncInputs = ServiceReflector.GetInputParameters(this.syncOperation.SyncMethod, false);
                this.taskInputs = ServiceReflector.GetInputParameters(this.taskOperation.TaskMethod, false);
            }
 
            public override void VerifyParameterLength()
            {
                if (this.syncInputs.Length != this.taskInputs.Length)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new InvalidOperationException(SR.GetString(SR.SyncTaskMatchConsistency_Parameters5,
                                                                   this.syncOperation.SyncMethod.Name,
                                                                   this.syncOperation.SyncMethod.DeclaringType,
                                                                   this.taskOperation.TaskMethod.Name,
                                                                   this.syncOperation.Name)));
                }
            }
 
            public override void VerifyParameterType()
            {
                for (int i = 0; i < this.syncInputs.Length; i++)
                {
                    if (this.syncInputs[i].ParameterType != this.taskInputs[i].ParameterType)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new InvalidOperationException(SR.GetString(SR.SyncTaskMatchConsistency_Parameters5,
                                                                       this.syncOperation.SyncMethod.Name,
                                                                       this.syncOperation.SyncMethod.DeclaringType,
                                                                       this.taskOperation.TaskMethod.Name,
                                                                       this.syncOperation.Name)));
                    }
                }
            }
 
            public override void VerifyReturnType()
            {
                if (this.syncOperation.SyncMethod.ReturnType != this.syncOperation.TaskTResult)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new InvalidOperationException(SR.GetString(SR.SyncTaskMatchConsistency_ReturnType5,
                                                                   this.syncOperation.SyncMethod.Name,
                                                                   this.syncOperation.SyncMethod.DeclaringType,
                                                                   this.taskOperation.TaskMethod.Name,
                                                                   this.syncOperation.Name)));
                }
            }
 
            public override void VerifyFaultContractAttribute()
            {
                if (this.taskOperation.Faults.Count != 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new InvalidOperationException(SR.GetString(SR.SyncTaskMatchConsistency_Attributes6,
                                                                   this.syncOperation.SyncMethod.Name,
                                                                   this.syncOperation.SyncMethod.DeclaringType,
                                                                   this.taskOperation.TaskMethod.Name,
                                                                   this.syncOperation.Name,
                                                                   typeof(FaultContractAttribute).Name)));
 
                }
            }
 
            public override void VerifyKnownTypeAttribute()
            {
                if (this.taskOperation.KnownTypes.Count != 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new InvalidOperationException(SR.GetString(SR.SyncTaskMatchConsistency_Attributes6,
                                                                   this.syncOperation.SyncMethod.Name,
                                                                   this.syncOperation.SyncMethod.DeclaringType,
                                                                   this.taskOperation.TaskMethod.Name,
                                                                   this.syncOperation.Name,
                                                                   typeof(ServiceKnownTypeAttribute).Name)));
                }
            }
 
            public override void VerifyIsOneWayStatus()
            {
                if (this.syncOperation.Messages.Count != this.taskOperation.Messages.Count)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new InvalidOperationException(SR.GetString(SR.SyncTaskMatchConsistency_Property6,
                                                                       this.syncOperation.SyncMethod.Name,
                                                                       this.syncOperation.SyncMethod.DeclaringType,
                                                                       this.taskOperation.TaskMethod.Name,
                                                                       this.syncOperation.Name,
                                                                       "IsOneWay")));
                }
            }
 
            public override void VerifyActionAndReplyAction()
            {
                for (int index = 0; index < this.syncOperation.Messages.Count; ++index)
                {
                    if (this.syncOperation.Messages[index].Action != this.taskOperation.Messages[index].Action)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new InvalidOperationException(SR.GetString(SR.SyncTaskMatchConsistency_Property6,
                                                                       this.syncOperation.SyncMethod.Name,
                                                                       this.syncOperation.SyncMethod.DeclaringType,
                                                                       this.taskOperation.TaskMethod.Name,
                                                                       this.syncOperation.Name,
                                                                       index == 0 ? "Action" : "ReplyAction")));
                    }
                }
            }
        }
 
        private class TaskAsyncOperationConsistencyVerifier : OperationConsistencyVerifier
        {
            OperationDescription taskOperation;
            OperationDescription asyncOperation;
            ParameterInfo[] taskInputs;
            ParameterInfo[] asyncInputs;
 
            public TaskAsyncOperationConsistencyVerifier(OperationDescription taskOperation, OperationDescription asyncOperation)
            {
                this.taskOperation = taskOperation;
                this.asyncOperation = asyncOperation;
                this.taskInputs = ServiceReflector.GetInputParameters(this.taskOperation.TaskMethod, false);
                this.asyncInputs = ServiceReflector.GetInputParameters(this.asyncOperation.BeginMethod, true);
            }
 
            public override void VerifyParameterLength()
            {
                if (this.taskInputs.Length != this.asyncInputs.Length)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new InvalidOperationException(SR.GetString(SR.TaskAsyncMatchConsistency_Parameters5,
                                                                   this.taskOperation.TaskMethod.Name,
                                                                   this.taskOperation.TaskMethod.DeclaringType,
                                                                   this.asyncOperation.BeginMethod.Name,
                                                                   this.asyncOperation.EndMethod.Name,
                                                                   this.taskOperation.Name)));
                }
            }
 
            public override void VerifyParameterType()
            {
                for (int i = 0; i < this.taskInputs.Length; i++)
                {
                    if (this.taskInputs[i].ParameterType != this.asyncInputs[i].ParameterType)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new InvalidOperationException(SR.GetString(SR.TaskAsyncMatchConsistency_Parameters5,
                                                                       this.taskOperation.TaskMethod.Name,
                                                                       this.taskOperation.TaskMethod.DeclaringType,
                                                                       this.asyncOperation.BeginMethod.Name,
                                                                       this.asyncOperation.EndMethod.Name,
                                                                       this.taskOperation.Name)));
                    }
                }
            }
 
            public override void VerifyReturnType()
            {
                if (this.taskOperation.TaskTResult != this.asyncOperation.EndMethod.ReturnType)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new InvalidOperationException(SR.GetString(SR.TaskAsyncMatchConsistency_ReturnType5,
                                                                   this.taskOperation.TaskMethod.Name,
                                                                   this.taskOperation.TaskMethod.DeclaringType,
                                                                   this.asyncOperation.BeginMethod.Name,
                                                                   this.asyncOperation.EndMethod.Name,
                                                                   this.taskOperation.Name)));
                }
            }
 
            public override void VerifyFaultContractAttribute()
            {
                if (this.asyncOperation.Faults.Count != 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new InvalidOperationException(SR.GetString(SR.TaskAsyncMatchConsistency_Attributes6,
                                                                   this.taskOperation.TaskMethod.Name,
                                                                   this.taskOperation.TaskMethod.DeclaringType,
                                                                   this.asyncOperation.BeginMethod.Name,
                                                                   this.asyncOperation.EndMethod.Name,
                                                                   this.taskOperation.Name,
                                                                   typeof(FaultContractAttribute).Name)));
 
                }
            }
 
            public override void VerifyKnownTypeAttribute()
            {
                if (this.asyncOperation.KnownTypes.Count != 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new InvalidOperationException(SR.GetString(SR.TaskAsyncMatchConsistency_Attributes6,
                                                                   this.taskOperation.TaskMethod.Name,
                                                                   this.taskOperation.TaskMethod.DeclaringType,
                                                                   this.asyncOperation.BeginMethod.Name,
                                                                   this.asyncOperation.EndMethod.Name,
                                                                   this.taskOperation.Name,
                                                                   typeof(ServiceKnownTypeAttribute).Name)));
                }
            }
 
            public override void VerifyIsOneWayStatus()
            {
                if (this.taskOperation.Messages.Count != this.asyncOperation.Messages.Count)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new InvalidOperationException(SR.GetString(SR.TaskAsyncMatchConsistency_Property6,
                                                                       this.taskOperation.TaskMethod.Name,
                                                                       this.taskOperation.TaskMethod.DeclaringType,
                                                                       this.asyncOperation.BeginMethod.Name,
                                                                       this.asyncOperation.EndMethod.Name,
                                                                       this.taskOperation.Name,
                                                                       "IsOneWay")));
                }
            }
 
            public override void VerifyActionAndReplyAction()
            {
                for (int index = 0; index < this.taskOperation.Messages.Count; ++index)
                {
                    if (this.taskOperation.Messages[index].Action != this.asyncOperation.Messages[index].Action)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new InvalidOperationException(SR.GetString(SR.TaskAsyncMatchConsistency_Property6,
                                                                       this.taskOperation.TaskMethod.Name,
                                                                       this.taskOperation.TaskMethod.DeclaringType,
                                                                       this.asyncOperation.BeginMethod.Name,
                                                                       this.asyncOperation.EndMethod.Name,
                                                                       this.taskOperation.Name,
                                                                       index == 0 ? "Action" : "ReplyAction")));
                    }
                }
            }
        }
 
        class ContractReflectionInfo
        {
            internal Type iface;
            internal Type callbackiface;
        }
 
        // This function factors out the logic of how programming model attributes interact with service inheritance.
        // See MB 37427 for details.
        //
        // To use this, just call ApplyServiceInheritance() with
        //  - the service type you want to pull behavior attributes from
        //  - the "destination" behavior collection, where all the right behavior attributes should be added to
        //  - a delegate
        // The delegate is just a function you write that behaves like this: 
        //    imagine that "currentType" was the only type (imagine there was no inheritance hierarchy)
        //    find desired behavior attributes on this type, and add them to "behaviors"
        // ApplyServiceInheritance then uses the logic you provide for getting behavior attributes from a single type, 
        // and it walks the actual type hierarchy and does the inheritance/override logic for you.
        public static void ApplyServiceInheritance<IBehavior, TBehaviorCollection>(
                     Type serviceType,
                     TBehaviorCollection descriptionBehaviors,
                     ServiceInheritanceCallback<IBehavior, TBehaviorCollection> callback)
            where IBehavior : class
            where TBehaviorCollection : KeyedByTypeCollection<IBehavior>
        {
            // work our way up the class hierarchy, looking for attributes; adding "bottom up" so that for each
            // type of attribute, we only pick up the bottom-most one (the one attached to most-derived class)
            for (Type currentType = serviceType; currentType != null; currentType = currentType.BaseType)
            {
                AddBehaviorsAtOneScope(currentType, descriptionBehaviors, callback);
            }
        }
 
        public delegate void ServiceInheritanceCallback<IBehavior, TBehaviorCollection>(Type currentType, KeyedByTypeCollection<IBehavior> behaviors);
 
        // To use this, just call AddBehaviorsAtOneScope() with
        //  - the type you want to pull behavior attributes from
        //  - the "destination" behavior collection, where all the right behavior attributes should be added to
        //  - a delegate
        // The delegate is just a function you write that behaves like this: 
        //    imagine that "currentType" was the only type (imagine there was no inheritance hierarchy)
        //    find desired behavior attributes on this type, and add them to "behaviors"
        // AddBehaviorsAtOneScope then uses the logic you provide for getting behavior attributes from a single type, 
        // and it does the override logic for you (only add the behavior if it wasn't already in the descriptionBehaviors)
        static void AddBehaviorsAtOneScope<IBehavior, TBehaviorCollection>(
                     Type type,
                     TBehaviorCollection descriptionBehaviors,
                     ServiceInheritanceCallback<IBehavior, TBehaviorCollection> callback)
            where IBehavior : class
            where TBehaviorCollection : KeyedByTypeCollection<IBehavior>
        {
            KeyedByTypeCollection<IBehavior> toAdd = new KeyedByTypeCollection<IBehavior>();
            callback(type, toAdd);
            // toAdd now contains the set of behaviors we'd add if this type (scope) were the only source of behaviors
 
            for (int i = 0; i < toAdd.Count; i++)
            {
                IBehavior behavior = toAdd[i];
                if (!descriptionBehaviors.Contains(behavior.GetType()))
                {
                    // if we didn't already see this type of attribute at a previous scope
                    // then it belongs in the final result
                    if (behavior is ServiceBehaviorAttribute || behavior is CallbackBehaviorAttribute)
                    {
                        descriptionBehaviors.Insert(0, behavior);
                    }
                    else
                    {
                        descriptionBehaviors.Add(behavior);
                    }
                }
            }
        }
    }
}