System.Exception.Data не будет сериализоваться в DataContract?
У меня есть некоторые службы WCF, использующие dataContracts, и я хотел передать исключение с пользовательскими данными Dictionary
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 ответа
System.Exception реализует ISerializable, который обрабатывается сериализатором так же, как словарь - он может быть [de] сериализован, но вы должны указать сериализатору, какие типы будут [de] сериализованы. В случае исключения вы не можете изменить объявление класса, поэтому, если вы хотите, чтобы этот сценарий работал, вам нужно добавить известные типы в контракт на обслуживание (используя [ServiceKnownType]) для обоих классов, которые будут используется для свойства Data (которое использует внутренний тип System.Collections.ListDictionaryInternal
) и для любых типов, которые вы добавите в словарь данных. Приведенный ниже код показывает, как это можно сделать (хотя я бы действительно советовал против этого, вам следует определить некоторые типы DTO, которые будут обрабатывать информацию, которая должна быть возвращена, чтобы избежать необходимости иметь дело с внутренними деталями реализации класса Exception,
public class Stackru_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();
}
}
Вы также должны будете добавить [KnownType]
атрибут для любого несистемного типа, который вы можете добавить к Dictionary<string , object>
, Так, например, если вы добавите MyType
в словарь, то вам нужно будет добавить [KnownType(typeof(MyType))]
,