File: System\ServiceModel\Dispatcher\FaultFormatter.cs
Web Access
Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
 
using System.Collections.Generic;
using System.ServiceModel.Channels;
using System.Runtime.Serialization;
using System.Xml;
using System.Runtime;
 
namespace System.ServiceModel.Dispatcher
{
    /*
     This class is not exposed on the contract as we only need it public for reflection purpose on .Net Native.
     */
    public class FaultFormatter : IClientFaultFormatter, IDispatchFaultFormatter
    {
        private FaultContractInfo[] _faultContractInfos;
 
        internal FaultFormatter(Type[] detailTypes)
        {
            List<FaultContractInfo> faultContractInfoList = new List<FaultContractInfo>();
            for (int i = 0; i < detailTypes.Length; i++)
            {
                faultContractInfoList.Add(new FaultContractInfo(MessageHeaders.WildcardAction, detailTypes[i]));
            }
 
            AddInfrastructureFaults(faultContractInfoList);
            _faultContractInfos = GetSortedArray(faultContractInfoList);
        }
 
        internal FaultFormatter(SynchronizedCollection<FaultContractInfo> faultContractInfoCollection)
        {
            List<FaultContractInfo> faultContractInfoList;
            lock (faultContractInfoCollection.SyncRoot)
            {
                faultContractInfoList = new List<FaultContractInfo>(faultContractInfoCollection);
            }
            AddInfrastructureFaults(faultContractInfoList);
            _faultContractInfos = GetSortedArray(faultContractInfoList);
        }
 
        public MessageFault Serialize(FaultException faultException, out string action)
        {
            XmlObjectSerializer serializer = null;
            Type detailType = null;
            string faultExceptionAction = action = faultException.Action;
 
            Type faultExceptionOfT = null;
            for (Type faultType = faultException.GetType(); faultType != typeof(FaultException); faultType = faultType.BaseType())
            {
                if (faultType.IsGenericType() && (faultType.GetGenericTypeDefinition() == typeof(FaultException<>)))
                {
                    faultExceptionOfT = faultType;
                    break;
                }
            }
            if (faultExceptionOfT != null)
            {
                detailType = faultExceptionOfT.GetGenericArguments()[0];
                serializer = GetSerializer(detailType, faultExceptionAction, out action);
            }
            return CreateMessageFault(serializer, faultException, detailType);
        }
 
        public FaultException Deserialize(MessageFault messageFault, string action)
        {
            if (!messageFault.HasDetail)
            {
                return new FaultException(messageFault, action);
            }
 
            return CreateFaultException(messageFault, action);
        }
 
        protected virtual XmlObjectSerializer GetSerializer(Type detailType, string faultExceptionAction, out string action)
        {
            action = faultExceptionAction;
            FaultContractInfo faultInfo = null;
            for (int i = 0; i < _faultContractInfos.Length; i++)
            {
                if (_faultContractInfos[i].Detail == detailType)
                {
                    faultInfo = _faultContractInfos[i];
                    break;
                }
            }
            if (faultInfo != null)
            {
                if (action == null)
                {
                    action = faultInfo.Action;
                }
 
                return faultInfo.Serializer;
            }
            else
            {
                return DataContractSerializerDefaults.CreateSerializer(detailType, int.MaxValue /* maxItemsInObjectGraph */ );
            }
        }
 
        protected virtual FaultException CreateFaultException(MessageFault messageFault, string action)
        {
            IList<FaultContractInfo> faultInfos;
            if (action != null)
            {
                faultInfos = new List<FaultContractInfo>();
                for (int i = 0; i < _faultContractInfos.Length; i++)
                {
                    if (_faultContractInfos[i].Action == action || _faultContractInfos[i].Action == MessageHeaders.WildcardAction)
                    {
                        faultInfos.Add(_faultContractInfos[i]);
                    }
                }
            }
            else
            {
                faultInfos = _faultContractInfos;
            }
 
            Type detailType = null;
            object detailObj = null;
            for (int i = 0; i < faultInfos.Count; i++)
            {
                FaultContractInfo faultInfo = faultInfos[i];
                XmlDictionaryReader detailReader = messageFault.GetReaderAtDetailContents();
                XmlObjectSerializer serializer = faultInfo.Serializer;
                if (serializer.IsStartObject(detailReader))
                {
                    detailType = faultInfo.Detail;
                    try
                    {
                        detailObj = serializer.ReadObject(detailReader);
                        FaultException faultException = CreateFaultException(messageFault, action,
                            detailObj, detailType, detailReader);
                        if (faultException != null)
                        {
                            return faultException;
                        }
                    }
                    catch (SerializationException)
                    {
                    }
                }
            }
            return new FaultException(messageFault, action);
        }
 
        protected FaultException CreateFaultException(MessageFault messageFault, string action,
            object detailObj, Type detailType, XmlDictionaryReader detailReader)
        {
            if (!detailReader.EOF)
            {
                detailReader.MoveToContent();
                if (detailReader.NodeType != XmlNodeType.EndElement && !detailReader.EOF)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SRP.ExtraContentIsPresentInFaultDetail));
                }
            }
            bool isDetailObjectValid = true;
            if (detailObj == null)
            {
                isDetailObjectValid = !detailType.IsValueType();
            }
            else
            {
                isDetailObjectValid = detailType.IsAssignableFrom(detailObj.GetType());
            }
            if (isDetailObjectValid)
            {
                Type knownFaultType = typeof(FaultException<>).MakeGenericType(detailType);
                return (FaultException)Activator.CreateInstance(knownFaultType,
                                                            detailObj,
                                                            messageFault.Reason,
                                                            messageFault.Code,
                                                            action);
            }
            return null;
        }
 
        private static FaultContractInfo[] GetSortedArray(List<FaultContractInfo> faultContractInfoList)
        {
            FaultContractInfo[] temp = faultContractInfoList.ToArray();
            Array.Sort(temp,
                delegate (FaultContractInfo x, FaultContractInfo y)
                { return string.CompareOrdinal(x.Action, y.Action); }
                );
            return temp;
        }
 
        private static void AddInfrastructureFaults(List<FaultContractInfo> faultContractInfos)
        {
            faultContractInfos.Add(new FaultContractInfo(FaultCodeConstants.Actions.NetDispatcher, typeof(ExceptionDetail)));
        }
 
        private static MessageFault CreateMessageFault(XmlObjectSerializer serializer, FaultException faultException, Type detailType)
        {
            if (detailType == null)
            {
                if (faultException.Fault != null)
                {
                    return faultException.Fault;
                }
 
                return MessageFault.CreateFault(faultException.Code, faultException.Reason);
            }
            Fx.Assert(serializer != null, "");
 
            Type operationFaultType = typeof(OperationFault<>).MakeGenericType(detailType);
            return (MessageFault)Activator.CreateInstance(operationFaultType, serializer, faultException);
        }
 
        /*
        This class is not exposed on the contract as we only need it public for reflection purpose on .Net Native.
        */
        public class OperationFault<T> : XmlObjectSerializerFault
        {
            public OperationFault(XmlObjectSerializer serializer, FaultException<T> faultException) :
                base(faultException.Code, faultException.Reason,
                      faultException.Detail,
                      serializer,
                      string.Empty/*actor*/,
                      string.Empty/*node*/)
            {
            }
        }
    }
}