【问题标题】:System.Exception.Data will not serialise on DataContract?System.Exception.Data 不会在 DataContract 上序列化?
【发布时间】:2011-07-01 19:30:14
【问题描述】:

我有一些使用 dataContracts 的 WCF 服务,我希望通过 Data 属性中的自定义 Dictionary 数据传递一个异常,但是当我在抛出之前在这个数组上添加任何数据时,我得到以下我的自定义 ServiceBehavior 的 ErrorHandler 中的错误:

Type 'System.Collections.ListDictionaryInternal'

带有数据合约名称 'ArrayOfKeyValueOfanyTypeanyType:http://schemas.microsoft.com/2003/10/Serialization/Arrays' 预计不会。添加任何类型不 静态已知到已知列表 类型 - 例如,通过使用 KnownTypeAttribute 属性或由 将它们添加到已知类型列表中 传递给 DataContractSerializer。

我是否总是需要创建一个带有注释为 DataContract 的 Dictionary 属性的自定义异常并抛出它?使用 ErrorHandler 的想法是避免在每个服务方法中处理异常,我还需要为方法添加进一步的注释吗?我错过了什么?

作为参考,这是我的 FaultErrorHandler 类:

public class FaultErrorHandler : BehaviorExtensionElement, IErrorHandler, IServiceBehavior
    {
        public bool HandleError(Exception error)
        {
            if (!Logger.IsLoggingEnabled()) return true;
            var logEntry = new LogEntry
            {
                EventId = 100,
                Severity = TraceEventType.Error,
                Priority = 1,
                Title = "WCF Failure",
                Message = string.Format("Error occurred: {0}", error)
            };
            logEntry.Categories.Add("MiddleTier");

            Logger.Write(logEntry);
            return true;
        }

        public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
        {
            var faultException = new FaultException<Exception>( error, new FaultReason(string.Format("System error occurred, exception: {0}", error)));
            var faultMessage = faultException.CreateMessageFault();
            fault = Message.CreateMessage(version, faultMessage, Schema.WebServiceStandard);
        }

        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, System.ServiceModel.ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcher chanDisp in serviceHostBase.ChannelDispatchers)
            {
                chanDisp.ErrorHandlers.Add(this);
            };
        }

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

        public override Type BehaviorType
        {
            get { return typeof(FaultErrorHandler); }
        }

        protected override object CreateBehavior()
        {
            return new FaultErrorHandler();
        }
    }

我的典型服务接口如下:

[ServiceContract(Name = "Service", Namespace = Schema.WebServiceStandard, SessionMode = SessionMode.Allowed)]
    public interface IService 
    {

        [OperationContract(Name = "GetSomething")]
        [FaultContract(typeof(ValidationFault))]
        LookupResult GetSomething();
    }

【问题讨论】:

    标签: wcf enterprise-library servicecontract faultcontract


    【解决方案1】:

    System.Exception 实现 ISerializable,它由序列化程序以与 Dictionary 相同的方式处理 - 它可以被 [de] 序列化,但您需要告诉序列化程序哪些类型将被 [de] 序列化。在异常的情况下,您不能更改类声明,因此如果您想让这个场景工作,您需要在服务合同中添加已知类型(使用 [ServiceKnownType]),这两个类将是用于 Data 属性(使用内部类型 System.Collections.ListDictionaryInternal)以及您将添加到数据字典中的任何类型。下面的代码显示了如何做到这一点(虽然我真的建议不要这样做,但您应该定义一些 DTO 类型来处理需要返回的信息,以防止不得不处理 Exception 类的内部实现细节.

    public class StackOverflow_6552443
    {
        [DataContract]
        [KnownType("GetKnownTypes")]
        public class MyDCWithException
        {
            [DataMember]
            public Exception myException;
    
            public static MyDCWithException GetInstance()
            {
                MyDCWithException result = new MyDCWithException();
                result.myException = new ArgumentException("Invalid value");
                result.myException.Data["someData"] = new Dictionary<string, object>
                {
                    { "One", 1 },
                    { "Two", 2 },
                    { "Three", 3 },
                };
                return result;
            }
    
            public static Type[] GetKnownTypes()
            {
                List<Type> result = new List<Type>();
                result.Add(typeof(ArgumentException));
                result.Add(typeof(Dictionary<string, object>));
                result.Add(typeof(IDictionary).Assembly.GetType("System.Collections.ListDictionaryInternal"));
                return result.ToArray();
            }
        }
        [ServiceContract]
        public interface ITest
        {
            [OperationContract]
            MyDCWithException GetDCWithException();
        }
        public class Service : ITest
        {
            public MyDCWithException GetDCWithException()
            {
                return MyDCWithException.GetInstance();
            }
        }
        public static void Test()
        {
            string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
            ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
            host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");
            host.Open();
            Console.WriteLine("Host opened");
    
            ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
            ITest proxy = factory.CreateChannel();
    
            Console.WriteLine(proxy.GetDCWithException());
    
            ((IClientChannel)proxy).Close();
            factory.Close();
    
            Console.Write("Press ENTER to close the host");
            Console.ReadLine();
            host.Close();
        }
    }
    

    【讨论】:

    • 那么,在我上面的服务接口上,我只需要在接口声明上添加 [serviceKnownType(typeof(Exception))] ?而已?每种方法要添加什么?
    • 不行,你需要为Exception序列化使用的所有类型添加已知类型。我已经用代码示例更新了帖子。
    【解决方案2】:

    您还必须为您可能添加到Dictionary&lt;string , object&gt; 的任何非系统类型添加[KnownType] 属性。因此,例如,如果您将MyType 添加到字典中,那么您将需要添加[KnownType(typeof(MyType))]

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多