我正在尝试为WCF客户端应用程序构建错误日志记录机制,以便它们可以将WCF上下文外部可能发生的任何异常报告回服务。
在这种情况下,我绝对保证所有客户端都是.NET。
我发现了大量有关如何配置其他方式(例如,为客户提供服务)的信息,文章和示例,但对于客户提供的服务却一无所获。
我希望在客户端上执行以下操作:
Dim i As Integer
Try
i = String.Empty
Catch ex As Exception
Client.Create.Call(Sub(S As IService)
S.Log(LogLevels.Error, ex)
End Sub)
End Try
FaultException(Of T)
不起作用;我尝试了这个:
Dim i As Integer
Try
i = String.Empty
Catch ex As Exception
Try
Throw New FaultException(Of Exception)(ex)
Catch fex As FaultException
Client.Create.Call(Sub(S As IService)
S.Log(LogLevels.Error, fex.Message, fex)
End Sub)
End Try
End Try
它返回自己的一个难以理解的错误消息:
“不希望使用数据协定名称为'InvalidCastException的'类型'System.InvalidCastException':http : //schemas.datacontract.org/2004/07/System '。请考虑使用DataContractResolver或将任何静态未知的类型添加到已知列表中类型-例如,通过使用KnownTypeAttribute属性或将它们添加到传递给DataContractSerializer的已知类型列表中。”
异常的二进制序列化也不起作用。反序列化时出现错误:
“输入流不是有效的二进制格式。”
我的代码:
Class Test
Public Sub Test
Dim i As Integer
Dim aEx As Byte()
Dim oEx As Exception
Try
i = String.Empty
Catch ex As Exception
aEx = ex.ToBytes
oEx = aEx.ToObject(Of Exception)()
End Try
End Sub
End Class
Public Module Generic
<Extension>
Public Function ToBytes(Of T)([Object] As T) As Byte()
Using oStream As New MemoryStream
With New BinaryFormatter
.Serialize(oStream, [Object])
End With
Return oStream.ToArray
End Using
End Function
<Extension>
Public Function ToObject(Of T)(Data As Byte()) As T
Using oStream As New MemoryStream
oStream.Write(Data, 0, Data.Length)
oStream.Seek(0, SeekOrigin.Begin)
With New BinaryFormatter
Return .Deserialize(oStream)
End With
End Using
End Function
End Module
如何通过网络发送此非WCF异常?
编辑
这是我的服务界面,为简洁起见进行了编辑:
<ServiceContract>
Public Interface IService
<OperationContract> Sub Log(Level As LogLevels, Message As String, Exception As Exception)
End Interface
好吧,那是一个半程。希望我带来一些TrailMix。
解决方案原来是Microsoft的红发继子,在此NetDataContractSerializer
简要提及。您可以在这里阅读有关我为何称其为“为什么”的信息。
Ron Jacobs帖子的底部(到Aaron Skonnard的帖子)有一个重要链接,该链接现已过期。我在这里找到了一个副本(评论很重要,请看一下)。看来蒂姆·斯科特(Tim Scott)也使用了亚伦(Aaron)的代码。
正是在蒂姆更彻底的解释中,我找到了圣杯。(我一直想这样做!)
Tim正在使用Attribute
装饰,但是仅当在服务器上使用CLR对象时,这些装饰才适用。在这种情况下,我们正在为客户工作。
因此,最终,此修复程序简单易用。只需使用NetDataContractSerializer
将打包System.Exception
为Byte()
,将数据发送到服务器,然后在其中反序列化即可。无需高呼,不需要硝烟的镜子,只需一眼即可。
这会降低性能,因此请尽量减少Exception
!
这是我最终得到的结果,然后是蒂姆针对可能需要它的任何人的代码:
System.Exception
从客户端发送到服务器
Public Class Client
Private Sub ClientTest()
Dim i As Integer
Try
i = String.Empty
Catch ex As Exception
Client.Create.Call(Sub(S As IService)
S.Log(LogLevels.Error, ex.Message, ex.ToBytes)
End Sub)
End Try
End Sub
End Class
<ServiceContract>
Public Interface IService
<OperationContract> Sub Log(Level As LogLevels, Message As String, Data As Byte())
End Interface
Public Class Service
Implements IService
Public Sub Log(Level As LogLevels, Message As String, Data As Byte()) Implements IService.Log
' Log4Net logger created separately, out of scope of this question '
Select Case Level
Case LogLevels.Debug : Main.Logger.Debug(Message, Data.ToException)
Case LogLevels.Info : Main.Logger.Info(Message, Data.ToException)
Case LogLevels.Warn : Main.Logger.Warn(Message, Data.ToException)
Case LogLevels.Error : Main.Logger.Error(Message, Data.ToException)
Case LogLevels.Fatal : Main.Logger.Fatal(Message, Data.ToException)
End Select
End Sub
End Class
Public Module Extensions
<Extension>
Public Function ToBytes(Instance As Exception) As Byte()
Using oStream As New MemoryStream()
With New NetDataContractSerializer
.Serialize(oStream, Instance)
End With
Return oStream.ToArray
End Using
End Function
<Extension>
Public Function ToException(Data As Byte()) As Exception
Using oStream As New MemoryStream(Data)
With New NetDataContractSerializer
Return .Deserialize(oStream)
End With
End Using
End Function
End Module
将CLR对象(包括泛型)发送给客户端
public class NetDataContractOperationBehavior : DataContractSerializerOperationBehavior
{
public NetDataContractOperationBehavior(OperationDescription operation)
: base(operation)
{
}
public NetDataContractOperationBehavior(OperationDescription operation, DataContractFormatAttribute dataContractFormatAttribute)
: base(operation, dataContractFormatAttribute)
{
}
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns,
IList<Type> knownTypes)
{
return new NetDataContractSerializer(name, ns);
}
public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name,
XmlDictionaryString ns, IList<Type> knownTypes)
{
return new NetDataContractSerializer(name, ns);
}
}
public class UseNetDataContractSerializerAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
{
}
public void ApplyClientBehavior(OperationDescription description,
System.ServiceModel.Dispatcher.ClientOperation proxy)
{
ReplaceDataContractSerializerOperationBehavior(description);
}
public void ApplyDispatchBehavior(OperationDescription description,
System.ServiceModel.Dispatcher.DispatchOperation dispatch)
{
ReplaceDataContractSerializerOperationBehavior(description);
}
public void Validate(OperationDescription description)
{
}
private static void ReplaceDataContractSerializerOperationBehavior( OperationDescription description)
{
DataContractSerializerOperationBehavior dcsOperationBehavior =
description.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dcsOperationBehavior != null)
{
description.Behaviors.Remove(dcsOperationBehavior);
description.Behaviors.Add(new NetDataContractOperationBehavior(description));
}
}
}
// Then in the service contract, to every method we added the UseNetDataContractSerializer attribute, like so:
[UseNetDataContractSerializer]
[OperationContractAttribute]
Company SaveCompany(CompanyUpdater companyUpdater);
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句