如何将受管异常从WCF客户端发送到服务器(用于日志记录)? [英] How to send a managed Exception from WCF client to server (for logging)?

查看:192
本文介绍了如何将受管异常从WCF客户端发送到服务器(用于日志记录)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为我的WCF客户端应用程序构建一个错误记录机制,所以他们可以将任何可能在WCF上下文中发生的异常报告回服务。



在这种情况下,我绝对保证所有的客户端都是.NET。



我正在找到大量关于如何配置的信息,文章和示例我们希望在客户端上做这样的事情:



b
$ b

  Dim i As Integer 

尝试
i = String.Empty

Catch ex作为异常
Client.Create.Call(Sub(S As IService)
S.Log(LogLevels.Error,ex)
End Sub)

End尝试

FaultException(Of T)不工作;我试过这个:

  Dim i As Integer 

尝试
i = String.Empty

Catch ex As Exception
尝试
抛出新的FaultException(异常)(ex)

捕获fex作为FaultException
Client.Create。 Call(Sub(S As IService)
S.Log(LogLevels.Error,fex.Message,fex)
End Sub)

结束尝试
结束尝试

它返回一个隐藏的隐藏的错误消息:


键入'System.InvalidCastException'与数据合约名称'InvalidCastException: http://schemas.datacontract.org/2004/07/System '不是预期的,考虑使用DataContractResolver或将所有不知道的类型静态添加到已知类型的列表中 - 例如,使用KnownTypeAttribute属性或者将它们添加到传递给Da的已知类型的列表中taContractSerializer。


异常的二进制序列化也不起作用;我在反序列化中收到错误:


输入流不是有效的二进制格式。


我的代码为:

 类测试
公开Sub Test
Dim i As Integer
Dim aEx As Byte()
Dim oEx As Exception

尝试
i = String.Empty

Catch ex As Exception
aEx = ex.ToBytes
oEx = aEx.ToObject(Of Exception)()

结束尝试
End Sub
结束类

公共模块Generic
< Extension>
公共函数ToBytes(Of T)([Object] As T)As Byte()
使用oStream作为新的MemoryStream
使用新的BinaryFormatter
.Serialize(oStream,[Object] )
结束

返回oStream.ToArray
结束使用
结束功能



<扩展> ;
公共函数ToObject(Of T)(Data As Byte())As T
使用oStream作为新的MemoryStream
oStream.Write(Data,0,Data.Length)
oStream

使用新的BinaryFormatter
返回.Deserialize(oStream)
结束
结束使用
结束函数
结束模块

如何在线上发送此非WCF异常?



编辑



这是我的服务界面,为简洁起见进行了修改:

 < ServiceContract> 
公共接口IService
< OperationContract>子日志(Level As LogLevels,Message As String,Exception As Exception)
结束接口


解决方案

那是一次半途而废。希望我带来一些TrailMix。



解决方案证明是微软的红头狮子, NetDataContractSerializer 简要介绍了此处。你可以阅读我为什么称之为这里



Ron Jacobs的帖子(到Aaron Skonnard的帖子)的底部有一个重要的链接,现在已经过期了。我发现了一个这里(评论很重要,看看)。看起来像 Tim Scott 使用了亚伦的代码略有变化。



在蒂姆的更彻底的解释中,我发现了我的圣杯。 (我一直想要这样做一段时间!)



Tim正在使用属性装饰,但那些仅在使用服务器上的CLR对象时才适用。在这种情况下,我们正在使用客户端



最后,修复程序的简洁性很好。只需使用 NetDataContractSerializer System.Exception 打包成 Byte(),将数据发送到服务器,然后将其反序列化。没有诵经,烟雾镜,需要眼睛。



有一个性能损失,所以保持你的异常 s至少!



这是我最后的结果,其次是Tim的代码,可能需要它的任何人:



发送一个 System.Exception 从客户端到服务器

 公共类客户端
Private Sub ClientTest()
Dim i As Integer

尝试
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尝试
End Sub
结束类



< ServiceContract>
公共接口IService
< OperationContract>子日志(级别为LogLevels,消息为字符串,数据为字节())
结束接口



公共类服务
实现IService

公共子日志(级别为LogLevels,消息为字符串,数据为字节())实现IService.Log
'Log4Net记录器单独创建,超出了此问题的范围'
选择案例级别
案例LogLevels.Debug:Main.Logger.Debug(Message,Data.ToException)
案例LogLevels.Info:Main.Logger.Info(Message,Data.ToException)
案例LogLevels .Warn:Main.Logger.Warn(Message,Data.ToException)
案例LogLevels.Error:Main.Logger.Error(Message,Data.ToException)
案例LogLevels.Fatal:Main.Logger.Fatal (Message,Data.ToException)
结束选择
End Sub
结束类



公共模块扩展
<分机>
公共函数ToBytes(Instance As Exception)As Byte()
使用oStream作为新的MemoryStream()
使用新的NetDataContractSerializer
.Serialize(oStream,Instance)
结束

返回oStream.ToArray
结束使用
结束功能



<扩展名>
公共函数ToException(作为Byte())作为异常
使用oStream作为新的MemoryStream(数据)
新的NetDataContractSerializer
返回.Deserialize(oStream)
结束
结束使用
结束功能
结束模块

将CLR对象(包括泛型)发送到客户端

  public class NetDataContractOperationBehavior:DataContractSerializerOperationBehavior 
{
public NetDataContractOperationBehavior(OperationDescription操作)
:base(operation)
{
}

public NetDataContractOperationBehavior(OperationDescription操作,DataContractFormatAttribute dataContractFormatAttribute)
: base(operation,dataContractFormatAttribute)
{
}

public override XmlObjectSerializer CreateSerializer(Type type,string name,string ns,
IList< Type> knownTypes)
{
返回新的NetDataContractSerializer(name,ns);
}

public override XmlObjectSerializer CreateSerializer(Type type,XmlDictionaryString name,
XmlDictionaryString ns,IList< Type> knownTypes)
{
返回新的NetDataContractSerializer名称,ns);
$


public class UseNetDataContractSerializerAttribute:Attribute,IOperationBehavior
{
public void AddBindingParameters(OperationDescription description,BindingParameterCollection参数)
{
}

public void ApplyClientBehavior(OperationDescription description,
System.ServiceModel.Dispatcher.ClientOperation proxy)
{
ReplaceDataContractSerializerOperationBehavior (描述);
}

public void ApplyDispatchBehavior(OperationDescription description,
System.ServiceModel.Dispatcher.DispatchOperation dispatch)
{
ReplaceDataContractSerializerOperationBehavior(description);
}

public void验证(OperationDescription描述)
{
}

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));
}
}
}


//然后在服务合同中,对于我们添加UseNetDataContractSerializer属性的每个方法,如下所示:
[UseNetDataContractSerializer]
[OperationContractAttribute]
公司保存公司(CompanyUpdater companyUpdater);


I'm trying to build an error logging mechanism for my WCF client applications, so they can report any exceptions that may occur outside of the WCF context back to the service.

I'm absolutely guaranteed in this situation that all clients will be .NET.

I'm finding a plethora of information, articles and examples on how to configure for going the other way (e.g. service to client), but nothing on client to service.

I wish to do something like this on the client:

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) doesn't work; I tried this:

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

It returns a hideously obscure error message of its own:

"Type 'System.InvalidCastException' with data contract name 'InvalidCastException:http://schemas.datacontract.org/2004/07/System' is not expected. Consider using a DataContractResolver or 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."

Binary serialization of the exception doesn't work either; I get an error at deserialization:

"The input stream is not a valid binary format."

My code for that:

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

How does one send this non-WCF exception across the wire?

EDIT

Here's my service interface, edited for brevity:

<ServiceContract>
Public Interface IService
  <OperationContract> Sub Log(Level As LogLevels, Message As String, Exception As Exception)
End Interface

解决方案

Well, that was a trip and a half. Wish I'd brought some TrailMix.

The solution turned out to be Microsoft's red-headed stepchild, NetDataContractSerializer, mentioned briefly here. You can read about why I call it that here.

There's an important link at the bottom of Ron Jacobs' post (to Aaron Skonnard's post) that's now expired. I found a copy of that here (the comments are important, have a look). It looks like Tim Scott used a slight variation of Aaron's code as well.

It was in Tim's more thorough explanation that I found my Holy Grail. (I've been wanting to do this for some time!)

Tim is using Attribute decorations, but those are applicable only when working with CLR objects at the server. In this case we're working at the client.

So in the end, the fix is beautiful in its simplicity. Just use NetDataContractSerializer to package up the System.Exception into a Byte(), send the data to the server, and then deserialize it there. No chanting, smoke-n-mirrors, eye-of-the-newt required.

There's a performance penalty, so keep your Exceptions to a minimum!

Here's what I ended up with, followed by Tim's code for anyone who may need it:

Send a System.Exception from client to server

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

Send CLR objects (including generics) to the client

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);

这篇关于如何将受管异常从WCF客户端发送到服务器(用于日志记录)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆