System.Exception.Data не будет сериализоваться в DataContract? - PullRequest
1 голос
/ 01 июля 2011

У меня есть некоторые сервисы WCF, использующие dataContracts, и я хотел, чтобы я надеялся передать исключение с пользовательскими данными Dictionary в свойстве Data, но когда я добавляю какие-либо данные в этот массив перед выдачей, я получаю следующееошибка в ErrorHandler моего пользовательского ServiceBehavior:

Type 'System.Collections.ListDictionaryInternal'

с именем контракта данных 'ArrayOfKeyValueOfanyTypeanyType: http://schemas.microsoft.com/2003/10/Serialization/Arrays' не ожидается.Добавьте любые типы, которые не известны статически, в список известных типов - например, с помощью атрибута KnownTypeAttribute или добавив их в список известных типов, передаваемых в DataContractSerializer.

Мне обязательно нужно создаватьпользовательское исключение со свойством Dictionary, аннотированным как DataContract и выбрасывающим это?Идея использования 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();
    }

Ответы [ 2 ]

4 голосов
/ 02 июля 2011

System.Exception реализует ISerializable, который обрабатывается сериализатором так же, как словарь - он может быть [де] сериализован, но вам нужно указать сериализатору, какие типы будут [де] сериализованы. В случае исключения вы не можете изменить объявление класса, поэтому, если вы хотите, чтобы этот сценарий работал, вам нужно добавить известные типы в контракт на обслуживание (используя [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();
    }
}
0 голосов
/ 05 июля 2011

Вам также нужно будет добавить атрибут [KnownType] для любого не системного типа, который вы можете добавить к Dictionary<string , object>.Так, например, если вы добавите MyType в словарь, то вам нужно будет добавить [KnownType(typeof(MyType))].

...