System.Exception.Data 不会在 DataContract 上序列化?
我有一些使用 dataContracts 的 WCF 服务,并且我希望通过自定义 Dictionary< 传递异常。字符串,对象> Data 属性中的数据,但是当我在抛出之前在此数组上添加任何数据时,我在自定义 ServiceBehavior 的 ErrorHandler 中收到以下错误:
类型“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();
}
I have some WCF services using dataContracts and i wanted to I was hoping to pass a Exception with custom Dictionary< string , object > data in the Data property, but when i add any data on this array before throwing i get the following error in the ErrorHandler of my custom ServiceBehavior:
Type 'System.Collections.ListDictionaryInternal'
with data contract name
'ArrayOfKeyValueOfanyTypeanyType:http://schemas.microsoft.com/2003/10/Serialization/Arrays'
is not expected. Add any types not
known statically to the list of known
types - for example, by using the
KnownTypeAttribute attribute or by
adding them to the list of known types
passed to DataContractSerializer.
Do i invariably need to create a custom exception with a Dictionary property annotated as a DataContract and throw that? the idea of using the ErrorHandler is avoiding to handle exceptions in each service method, do i still need to add further annotations to the methods? what i am missing?
for reference, this is my FaultErrorHandler class:
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();
}
}
my typical service interface looks like:
[ServiceContract(Name = "Service", Namespace = Schema.WebServiceStandard, SessionMode = SessionMode.Allowed)]
public interface IService
{
[OperationContract(Name = "GetSomething")]
[FaultContract(typeof(ValidationFault))]
LookupResult GetSomething();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
System.Exception 实现了 ISerialized,它由序列化器以与字典相同的方式处理 - 它可以被[反]序列化,但您需要告诉序列化器哪些类型将被[反]序列化。在出现异常的情况下,您无法更改类声明,因此如果您想让此方案发挥作用,您需要在服务契约中添加已知类型(使用 [ServiceKnownType]),这两个类将是用于 Data 属性(使用内部类型
System.Collections.ListDictionaryInternal
)以及要添加到数据字典中的任何类型。下面的代码显示了如何做到这一点(尽管我真的建议反对,您应该定义一些 DTO 类型来处理需要返回的信息,以防止必须处理 Exception 类的内部实现细节。System.Exception implements ISerializable, which is handled by the serializer the same way as a Dictionary is - it can be [de]serialized, but you need to tell the serializer which types are going to be [de]serialized. In the case of exception, you can't change the class declaration, so if you want to make this scenario work, you'll need to add known types in your service contract (using [ServiceKnownType]) for both the class which will be used for the Data property (which uses the internal type
System.Collections.ListDictionaryInternal
) and for any types that you'll add to the data dictionary. The code below shows how this can be done (although I'd really advice against that, you should define some DTO types which would handle the information which needs to be returned, to prevent having to deal with the internal implementation details of the Exception class.您还必须为可能添加到
Dictionary
中的任何非系统类型添加[KnownType]
属性。例如,如果您将MyType
添加到字典中,那么您将需要添加[KnownType(typeof(MyType))]
。You will also have to add a
[KnownType]
attribute for any non-system type that you might add to yourDictionary<string , object>
. So for example if you add aMyType
to the dictionary, then you will need to add[KnownType(typeof(MyType))]
.