现在的位置: 首页 > 综合 > 正文

再说WCF Data Contract KnownTypeAttribute

2011年12月22日 ⁄ 综合 ⁄ 共 3582字 ⁄ 字号 评论关闭

        WCF 中的序列化是用DataContractSerializer,所有被[DataContract][DataMemeber]标记的类和属性会被DataContractSerializer序列化。在WCF中使用Contract模式来分辨和指定序列化/反序列化的类型,它是通过http://xmlns/Class这样的命名空间来标识这个序列化的对象的,一旦在序列化过程中无法找到这样的标识(比如某个字段,或者子对象)时,序列化就会失败。KnownTypeAttribute就提供了为我们通知序列化器去寻找未知对象的映射的途径。在Remoting中这样的问题不会存在,因为Remoting实际上是通过将一个类型传递给双方来进行类型匹配的。

 

         那么KnowTypeAttribute到底用在什么地方呢?上边说了,当前类的未知类型。那什么又是当前类的未知类型呢?或许说未知类型有些不恰当,但下边的实际应用可能会让你更清楚这到底是指什么。

1). 序列化对象是从期望返回的类型继承;

2). 无法确定当前所使用类型的。例如Object类型,或者接口类型,你需要告诉序列化器去寻找确切的类来进行序列化。

3). 使用泛型类型作为期望返回类型的;

4). 使用像ArrayList等以object为基础类型存储对象的;

 

          下边我们以第一种类型为例说明KnownTypeAttribute的用法。序列化对象一般是参与到在服务端和客户端传递的数据。在面向对象的设计中,继承可以很好的解决很多业务问题,并简化处理。而在下边的例子中我们可以看出KnownType到底能够做什么。

namespace WcfServiceDemo

{

    [DataContract]

    public class Address

    {

        public Address()

        {

             ……

        }

 

        [DataMember]

        public string AddressCategory { get; set; }

 

        [DataMember]

        public string AddressTitle { get; set; }

 

        [DataMember]

        public string AddressDetail { get; set; }

    }

 

    [DataContract]

    public class BusinessAddress : Address

    {

        ……

    }

 

    [DataContract]

    public class HomeAddress : Address

    {

        ……

    }

}

 

public class Service1 : IService1

{

    public Address GetAddress(bool isHome)

    {

        if (isHome)

            return new HomeAddress();

        else

            return new BusinessAddress();

    }

}

 

         上边的代码中简单的生命了三个数据契约类型,Address作为基类,HomeAddressBusinessAddress继承于Address类。在Service实现中,简单的通过一个参数来判断到底返回哪个子类。我们可以简单的写个客户端来调用一下这个服务,但很快你会发现浏览器(客户端)返回给你了错误:

Server Error in '/' Application.


The underlying connection was closed: The connection was closed unexpectedly.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Net.WebException: The underlying connection was closed: The connection was closed unexpectedly.

 

        很显然,这个错误并不能清楚的反应具体发生了什么,如果你跟进服务你会发现,所有的操作都正常,只是在返回数据的时候发生了错误。如果你愿意,你可以用DataContractSerializer类的一些方法来将一个HomeAddress序列化并反序列化为一个Address类型,你就会看到这个过程中发生了错误,序列化都无法进行。为什么呢?原因是HomeAddress/BusinessAddress的类型标识(如http://www.demo.com/BusinessAddress)无法序列化Address类型时匹配,它就不知道该把它序列化成哪个确切的类型。

 

        解决方法,给Address添加KnownTypeAttribute标识,当一个HomeAddress对象或者BusinessAddress对象被传递到Address进行序列化时,序列化器认识这个对象并能根据契约来进行序列化。

[DataContract]

[KnownType(typeof(BusinessAddress))]

[KnownType(typeof(HomeAddress))]

public class Address

{

      ……

}

 

    再次调用客户端(注意:如果你是通过Service Reference来引用服务的,那你必须在编译完服务端后选择Update Service Reference来更新服务引用,否则你的变化不会反应到客户端调用),现在你应该可以看到结果了。对于KnownTypeAttribute它还有一个可以替换的选择ServiceKnownTypeAttribute,你可以将它应用到一个Service或者一个Operation(他们的区别是:当把ServiceKnownType标记给以个Service,那么在这个ServiceKnownType都发生作用;而对于Operation则只在这个Operation时有效,即作用域不同。)。以下代码同样解决了上述问题:

[OperationContract]

[ServiceKnownType(typeof(BusinessAddress))]

[ServiceKnownType(typeof(HomeAddress))]

Address GetAddress(bool isHome);

    KnownType的另外一种标识方式是不用加代码的,在配置文件中通过声明型编程实现:

<system.runtime.serialization>

  <dataContractSerializer>

    <declaredTypes>

      <add type="WcfServiceDemo.Address, MyAssembly, Version=2.0.0.0, Culture=neutral,PublicKeyToken=null, processorArchitecture=MSIL">

        <knownType type="MyCompany.Library.Circle, MyAssembly, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null, processorArchitecture=MSIL"/>

      </add>

    </declaredTypes>

  </dataContractSerializer>

</system.runtime.serialization>

 

    而对于泛型来说,KnownType是不能被直接应用的,如果你写成[KnownType(typeof(BusinessAddress<>))]这样是不允许,但CLR给我们提供了另外一种方法来实现对序列化的通知:

[KnownType("GetKnownTypes")]

public class Address<T>

{       

    static Type[] GetKnownTypes()

    {

        return new Type[] { typeof(HomeAddress<T>) ,typeof(BusinessAddress)};

    }

}

    我们指定寻找K

抱歉!评论已关闭.