使用已知类型通过WCF传递复杂类型

时间:2012-06-22 16:06:06

标签: c# wcf

我正在开发WCF服务。

其中一个OperationContracts将复杂类型作为参数之一(System.Exception是特定的)。

当客户端调用代理时,如果我创建新的异常,例如

 System.Exception toNewException = new Exception();

发送它......它的效果相当不错。

但是如果我尝试发送'System.Web.HttpUnhandledException'类型的异常。服务故障类型未知。 (见下面的错误)

  

尝试序列化参数时出错   http://tempuri.org/:Exception。 InnerException消息是'Type   带有数据协定名称的'System.Web.HttpUnhandledException'   'HttpUnhandledException:HTTP://schemas.datacontract.org/2004/07/System.Web'   不是预期的。将任何静态未知的类型添加到列表中   已知类型 - 例如,通过使用KnownTypeAttribute属性   或者将它们添加到传递给的已知类型列表中   DataContractSerializer的。“。有关详细信息,请参阅InnerException。

我已经研究过已知的类型属性......但到目前为止我还没有找到任何有帮助的例子或文档。

任何人都知道有关我如何能够改变我的客户端/服务器以接受/处理任何异常的信息的良好来源?

让我的问题更加详细......错误说明; '将任何已知类型的静态已知类型添加到已知类型中。这就是我想知道如何专门处理复杂的.net对象,如system.exception。

更新

我尝试进行以下更改:

[OperationContract]
[ServiceKnownType(typeof(HttpUnhandledException))]
void LogError(Exception Exception, Boolean LogInternalFlag, int UserId, int ApplicationId, int SeverityId);

我最终得到了另一个错误!

  

尝试序列化参数时出错   http://tempuri.org/:Exception。 InnerException消息是'Type   带有数据协定名称的'System.Collections.ListDictionaryInternal'   'ArrayOfKeyValueOfanyTypeanyType:HTTP://schemas.microsoft.com/2003/10/Serialization/Arrays'   不是预期的。将任何静态未知的类型添加到列表中   已知类型 - 例如,通过使用KnownTypeAttribute属性   或者将它们添加到传递给的已知类型列表中   DataContractSerializer的。“。有关详细信息,请参阅InnerException。

除了有意义的错误之外还有错误......我不确定这意味着什么。


我还要投入,我确实尝试将异常序列化为xml并将其作为字符串传递...遗憾的是,这并不像听起来那么简单,并且需要考虑很多因素用于序列化异常和任何内部异常。

3 个答案:

答案 0 :(得分:2)

问题不在于类型复杂。您的问题是您的合同是使用基类(Exception)声明的,并且您传入了子类(HttpUnhandledException)。您未向合同序列化程序提供子项添加到基本定义的任何类型信息。您可以通过KnownTypes提供额外信息。

我在MSDN上的KnowTypes上看到的最佳文档是here

您在搜索中缺少的是因为您只有一个OperationContract(没有DataContract),您真正想要使用的属性是ServiceKnownType

[ServiceContract]
public interface IContract
{
   [OperationContract]
   [ServiceKnownType(typeof(HttpUnhandledException))]
   void PassException(Exception c);
}

您可以将属性设置为方法或整个界面。

注意,如果您在编译时不知道所有可能的子(Exception)类型,则会有一个ServiceKnownType(和KnowTypes)版本采用运行时方法。

答案 1 :(得分:1)

您可以通过将异常序列化为字节数组,然后在服务器端反序列化来解决此问题。

客户端,您可以使用:

    try { /* Do something that generates an exception here. */ }
    catch(System.Exception exception)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(ms, exception);
            YourWCFMethodHere(ms.ToArray());
        }
     }

然后在服务器端:

    public void YourWCFMethodHere(byte[] exception)
    {
        using (MemoryStream ms = new MemoryStream(exception, false))
        {
            BinaryFormatter bf = new BinaryFormatter();
            Exception ex = (Exception)bf.Deserialize(ms);
            // Do something with the exception here
        }
    }

当然,您需要进行一些错误检查,以防传入的byte []实际上不是Exception。

答案 2 :(得分:0)

ErnieL有正确的技术答案。但是,实际上,我倾向于1)打破异常的部分并仅传递那些或2)创建自己的自定义ErrorLog类型并发送它。

这消除了处理异常对象的子类化的需要,并为您提供了一致的界面。可能,您只需要异常中的一些成员(错误消息,内部异常错误消息和堆栈跟踪)。

对于内部异常,只需编写一个循环遍历消息并将它们连接在一起的方法。他们真的需要分开存放吗?