为什么我以编程方式添加了FaultContract无法识别?

时间:2013-12-02 16:39:00

标签: wcf-behaviour

我尝试让我的WCF服务始终抛出详细的错误,即使没有明确抛出它们也是如此。为实现这一目标,我实施了:

  • 一个ErrorHandler,其IErrorHandler.ProvideFault将非故障错误包装为FaultException

  • ServiceBehavior扩展,附加此处理程序并向每个操作添加此FaultException的错误描述,因此客户端可能会捕获它。

我使用错误处理程序属性修饰了我的服务(最初我有两个不同的IServiceBehavior实现,用于ErrorHandler和Operation.Faults)。

我还确保新的FaultDescription中的数据集与我在合同中定义FaultContract时检查的数据相同。

无论我尝试什么,当我在合同上使用FaultContract作为属性时,客户端正在正确捕获错误,但是当它在运行时通过ApplyDispatchBehavior附加时,只捕获了一般的FaultException。显然,其他一切(错误包装和抛出)都在工作,只有在运行时向每个操作添加一个FaultContract失败。

请帮忙......

这是代码:

ErrorHandling.cs

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text;
using Shared.Contracts.Faults;

namespace Server.WcfExtensions
{
    public class MyErrorHandler : IErrorHandler
    {
        #region IErrorHandler Members

        public bool HandleError(Exception error)
        {
            return false;
        }

        public void ProvideFault(Exception error, MessageVersion version, ref Message fault)   
        {
            if (error is FaultException) return;
            if (!error.GetType().IsSerializable) return;

            FaultException<GeneralServerFault> faultExc = new    FaultException<GeneralServerFault>(new GeneralServerFault(error), new FaultReason("Server Level Error"));
            MessageFault messageFault = faultExc.CreateMessageFault();
            fault = Message.CreateMessage(version, messageFault, faultExc.Action);
        }

        #endregion
    }

    class ErrorHandler : Attribute, IServiceBehavior
    {
        Type M_ErrorHandlerType;

        public Type ErrorHandlerType
        {
            get { return M_ErrorHandlerType; }
            set { M_ErrorHandlerType = value; }
        }

        #region IServiceBehavior Members

        public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            IErrorHandler errorHandler;
            try
            {
                errorHandler = (IErrorHandler)Activator.CreateInstance(ErrorHandlerType);
            }
            catch (MissingMethodException e)
            {
                throw new ArgumentException("Must have a public empty constructor.", e);
            }
            catch (InvalidCastException e)
            {
                throw new ArgumentException("Must implement IErrorHandler.", e);
            }

            foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
            {
                ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
                channelDispatcher.ErrorHandlers.Add(errorHandler);
            }

            foreach (ServiceEndpoint ep in serviceDescription.Endpoints)
            {
                foreach (OperationDescription opDesc in ep.Contract.Operations)
                {
                    Type t = typeof(GeneralServerFault);
                    string name = t.Name;

                    FaultDescription faultDescription = new FaultDescription(ep.Contract.Namespace + "/" + ep.Contract.Name + "/" + opDesc.Name + name + "Fault");
                    faultDescription.Name = name + "Fault";
                    faultDescription.Namespace = ep.Contract.Namespace;
                    faultDescription.DetailType = t;
                    opDesc.Faults.Add(faultDescription);
                }
            }
        }

        public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
        {
        }

        #endregion
    }
}

GeneralServerFault.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace Shared.Contracts.Faults
{
    [DataContract] //[Serializable]
    public class GeneralServerFault
    {
        [DataMember]
        public SerializableException Wrapped
        {
            get;
            private set;
        }

        public GeneralServerFault()
            : base()
        {
            Wrapped = new SerializableException();
        }

        public GeneralServerFault(Exception toWrap)
            : base()
        {
            Wrapped = new SerializableException(toWrap);
        }
    }

    [Serializable]
    public class SerializableException
    {
        public string Type { get; set; }
        public DateTime TimeStamp { get; set; }
        public string Message { get; set; }
        public string StackTrace { get; set; }

        public SerializableException()
        {
            this.TimeStamp = DateTime.Now;
        }

        public SerializableException(string Message)
            : this()
        {
            this.Message = Message;
        }

        public SerializableException(System.Exception ex)
            : this(ex.Message)
        {    
            if (ex == null) return;
            Type = ex.GetType().ToString();
            this.StackTrace = ex.StackTrace;
        }

        public override string ToString()
        {
            return this.Type + " " + this.Message + this.StackTrace;
        }
    }
}

IContractService.cs

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.ServiceModel;
using Shared.Contracts.Faults;

namespace Shared
{
    internal static class Namespaces
    {
        internal static class Contracts
        {
            public const string ServiceContracts = "http://mycompany/services";
        }
    }

    [ServiceContract(Namespace = Namespaces.Contracts.ServiceContracts, SessionMode = SessionMode.Required)]
    public interface IContactServices
    {
        [OperationContract]
        [FaultContract(typeof(DataNotFoundFault))]
        //[FaultContract(typeof(GeneralServerFault))]
        void DoSomething();
    }
}

ContractService.cs

using System;
using System.Collections.Generic;
using System.ServiceModel;
using Shared;
using Shared.Contracts.Faults;
using Server.WcfExtensions;

namespace Server.Services
{
    [ErrorHandler(ErrorHandlerType = typeof(MyErrorHandler))]
    public class ContactSevices : IContactServices
    {
        [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
        public void DoSomething()
        {
            throw new InvalidCastException("bla");
        }
    }
}

我省略了客户端和主机的代码

0 个答案:

没有答案