WCF初探-18:WCF数据协定之KnownType

KnownTypeAttribute 类概述

  • 在数据到达接收终结点时,WCF 运行库尝试将数据反序列化为公共语言运行库 (CLR) 类型的实例。通过首先检查传入消息选择为反序列化而实例化的类型,以确定消息内容遵循的数据协定。然后反序列化引擎尝试查找实现与消息内容兼容的数据协定的 CLR 类型。反序列化引擎在此过程中允许的侯选类型集称为反序列化程序的“已知类型”集。
  • 让反序列化引擎了解某个类型的一种方法是使用 KnownTypeAttribute。不能将属性应用于单个数据成员,只能将它应用于整个数据协定类型。将属性应用于可能为类或结构的“外部类型”。在其最基本的用法中,应用属性会将类型指定为“已知类型”。只要反序列化外部类型的对象或通过其成员引用的任何对象,这就会导致已知类型成为已知类型集的一部分。可以将多个 KnownTypeAttribute 属性应用于同一类型。

以下情形需要使用KnownTypeAttribute修饰数据协定

  • 已发送的数据协定源自预期的数据协定。在该情况下,传输的数据没有与接收终结点所预期相同的数据协定。
  • 要传输的信息的声明类型是接口,而非类、结构或枚举。因此,无法预先知道实际发送了实现接口的哪个类型,接收终结点就无法预先确定已传输数据的数据协定。
  • 要传输的信息的声明类型是 Object。由于每个类型都继承自 Object,而且无法预先知道实际发送了哪个类型,因此接收终结点无法预先确定已传输数据的数据协定。这是第一个项的特殊情况:每个数据协定都源自为 Object 生成的默认空数据协定。
  • 某些类型(包括 .NET Framework 类型)具有上述三种类别之一中的成员。例如,Hashtable 使用 Object 在哈希表中存储实际对象。在序列化这些类型时,接收方无法预先确定这些成员的数据协定。

数据协定使用KnownType示例

  • 解决方案如下:

  

  • 工程结构说明:
  1. Service:类库程序,WCF服务端程序。在服务协定接口IUserInfo.cs中定义数据协定类Person,再定义一个数据协定类User。User派生至Person,继承基类Person的构造方法,定义新的属性成员SayHello。定义操作协定GetInfo和GetInfoEx,两者返回类型都为Person。在UserInfo.cs中实现数据协定,在GetInfo中,我们返回Person对象类型,在GetInfoEx中我们返回派生类User类型。由于在GetInfoEx中我们需要传递派生类User类型,所以要在基类数据协定Person上面加上KnownType(typeof(User))特性标记,这样User就能够客户端进行反序列化,供客户端使用。

IUserInfo.cs代码如下: 

using System.ServiceModel;
using System.Runtime.Serialization;
using System;


namespace Service
{
[ServiceContract]
public interface IUserInfo
{
[OperationContract]
Person GetInfo(int id,string name);


[OperationContract]
Person GetInfoEx(int id, string name);
}


[DataContract]
[KnownType(typeof(User))]
public class Person
{
[DataMember]
public int ID { get; set; }


[DataMember]
public string Name { get; set; }


public Person(int id, string name)
{
this.ID = id;
this.Name = name;
}
}


[DataContract]
public class User:Person
{
public User(int id, string name): base(id,name){}


[DataMember]
public string SayHello
{
get { return "Hello:" + Name; }
set { throw new NotImplementedException(); }
}
}

}

  UserInfo.cs代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Service
{
public class UserInfo:IUserInfo
{
public Person GetInfo(int id, string name)
{
return new Person(id, name);
}

public Person GetInfoEx(int id, string name)
{
return new User(id, name);
}
}
}

  2.  Host:控制台应用程序,服务承载程序。添加对程序集Service的引用,完成以下代码,寄宿服务。Program.cs代码如下:

  

using System;
using System.ServiceModel;
using Service;

namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof(UserInfo)))
            {
                host.Opened += delegate { Console.WriteLine("服务已经启动,按任意键终止!"); };
                host.Open();
                Console.Read();
            }
        }
    }
}

  App.config代码如下:

  

<?xml version="1.0"?>
<configuration>
    <system.serviceModel>

        <services>
            <service name="Service.UserInfo" behaviorConfiguration="mexBehavior">
                <host>
                    <baseAddresses>
                        <add baseAddress="http://localhost:1234/UserInfo/"/>
                    </baseAddresses>
                </host>
                <endpoint address="" binding="wsHttpBinding" contract="Service.IUserInfo" />
                <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
            </service>
        </services>

        <behaviors>
            <serviceBehaviors>
                <behavior name="mexBehavior">
                    <serviceMetadata httpGetEnabled="true"/>
                    <serviceDebug includeExceptionDetailInFaults="true"/>
                </behavior>
            </serviceBehaviors>
        </behaviors>
    </system.serviceModel>
</configuration>

  我们通过svcutil.exe工具生成客户端代理类和客户端的配置文件

  svcutil.exe是一个命令行工具,位于路径C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin下,我们可以通过命令行运行该工具生成客户端代理类

  • 在运行中输入cmd打开命令行,输入 cd C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin
  • 输入svcutil.exe /out:f:\UserInfoClient.cs /config:f:\App.config http://localhost:1234/UserInfo

  3.  Client:控制台应用程序,客户端调用程序。将生成的UserInfoClient.cs和App.config复制到Client的工程目录下,完成客户端调用代码。Program.cs的代码如下:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Service;


namespace Client
{
class Program
{
static void Main(string[] args)
{
try
{
UserInfoClient proxy = new UserInfoClient();
Person P1 = proxy.GetInfo(1,"JACK");
Person P2 = proxy.GetInfoEx(2, "TOM");
Console.WriteLine("{0,-10}{1,-20}", "ID", "Message");


Console.WriteLine("{0,-10}{1,-20}", P1.ID, P1.Name);
if (P2 is User)
{
Console.WriteLine("{0,-10}{1,-20}", ((User)P2).ID,((User)P2).SayHello);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}


Console.Read();
}
}
}

  4.  程序运行效果如下:

  

总结:

  • 从上面的示例可以看出,如果我们需要使用的数据契约类派生出了子类,并且在操作协定中使用了子类数据契约类型和基类数据契约类型,那我们必须为子类和基类成员添加必要的DataContract和DataMember修饰,而且还需要在基类数据契约上加上KnownType来告知客户端需要反序列化子类的成员。
时间: 2024-10-11 15:31:52

WCF初探-18:WCF数据协定之KnownType的相关文章

我们一起学习WCF 第五篇数据协定和消息协定(附上源码)

A:数据协定(“数据协定”是在服务与客户端之间达成的正式协议,用于以抽象方式描述要交换的数据. 也就是说,为了进行通信,客户端和服务不必共享相同的类型,而只需共享相同的数据协定. 数据协定为每个参数或返回类型精确定义为进行交换而序列化哪些数据(将哪些数据转换为 XML)摘自MSDN)也就说数据协定是客户端和服务端之间达成的数据协议,相互通信的参数会被序列化然后进行传输.那么我用图来表示为什么用数据协定. 下面用图来说明其几个用处 那么下面我们来看看数据协定的代码实现 1:创建一个协定类 1 [D

WCF初探-17:WCF数据协定之等效性

数据协定等效性特点概述 对于客户端要将某种类型的数据成功发送到服务,或者服务要将数据成功发送到客户端的情况,接收端上并不一定必须存在此发送数据类型. 唯一的要求是两种类型的数据协定应该等效. 要使数据协定等效,其命名空间和名称必须相同. 此外,某一端上的每个数据成员还必须在另一端上具有等效的数据成员.注意,数据协定名称和命名空间以及数据成员名称均区分大小写. 要使数据成员等效,其名称必须相同. 此外,它们还必须表示同一类型的数据,也就是说,其数据协定必须等效. 如果同一端(发送方或接收方)存在两

WCF初探-16:WCF数据协定之基础知识

数据协定概念 “数据协定”是在服务与客户端之间达成的正式协议,用于以抽象方式描述要交换的数据. 也就是说,为了进行通信,客户端和服务不必共享相同的类型,而只需共享相同的数据协定. 数据协定为每一个做数据交换时需要被序列化的参数或者返回值做了精确定义. 数据协定特点 默认情况下, WCF使用称为数据协定序列化程序的序列化引擎对数据进行序列化和反序列化(与 XML 进行相互转换). 所有 .NET Framework 基元类型(如整型和字符串型)以及某些被视为基元的类型(如 DateTime 和 X

WCF基础之数据协定

数据协定最重要的当然就是DataContract和DataMember.这两个特性能应用到类.结构和枚举.这个两个特性跟服务契约的特点是一样的,只有被DataContract标记的类和类中被标记DataMember的属性.字段和事件才能被wcf的序列化引擎进行序列化和反序列化. [DataContract] public class UserInfoModel { private int age; [DataMember] public int Age { get { return age; }

WCF初探-20:WCF错误协定

WCF错误协定概述 在所有托管应用程序中,处理错误由 Exception 对象表示. 在基于 SOAP 的应用程序(如 WCF 应用程序)中,服务方法使用 SOAP 错误消息来传递处理错误信息. SOAP 错误是包括在服务操作元数据中的消息类型,因此会创建一个错误协定,客户端可使用该协定来使操作更加可靠或更具交互性. 此外,由于 SOAP 错误在客户端以 XML 格式表示,这是一种任何 SOAP 平台上的客户端都可以使用的具有极好的互操作性的类型系统,可增加 WCF 应用程序的适用范围. 由于

WCF初探-14:WCF服务协定

前言: 在前面的文章中,我们定义的服务协定上都会有一个ServiceContract的特性来修饰,这是因为服务契约的实现要靠ServiceContractAttribute 属性定义,然后使用一个或多个类(或接口)方法中的 OperationContractAttribute 属性定义协定的服务操作. 实现服务协定后并将其与WCF 绑定和 EndpointAddress 对象一起使用时,此服务协定将公开以供客户端使用. 公开的信息由 ServiceContractAttribute 表示,其接口

WCF初探-15:WCF操作协定

前言: 在前面的文章中,我们定义服务协定时,在它的操作方法上都会加上OperationContract特性,此特性属于OperationContractAttribute 类,将OperationContract应用于方法,以指示该方法实现作为服务协定(由 ServiceContractAttribute 属性指定)一部分的服务操作.OperationContractAttribute 属性声明方法是服务协定中的操作. 只有具有 OperationContractAttribute 属性的方法可

WCF初探-10:WCF客户端调用服务

创建WCF 服务客户端应用程序需要执行下列步骤: 获取服务终结点的服务协定.绑定以及地址信息 使用该信息创建 WCF 客户端 调用操作 关闭该 WCF 客户端对象 WCF客户端调用服务存在以下特点: 服务和客户端使用托管属性.接口和方法对协定进行建模. 若要连接客户端应用程序中的服务,则需要获取该服务协定的类型信息.通常,我们使用Svcutil.exe(ServiceModel Metadata Utility Tool)来完成,也可以直接在客户端项目上引用服务地址完成.它们会从服务中下载元数据

WCF初探-26:WCF中的会话

理解WCF中的会话机制 在WCF应用程序中,会话将一组消息相互关联,从而形成对话.会话”是在两个终结点之间发送的所有消息的一种相互关系.当某个服务协定指定它需要会话时,该协定会指定所有调用(即,支持调用的基础消息交换)必须是同一对话的一部分.如果某个协定指定它允许使用会话但不要求使用会话,则客户端可以进行连接,并选择建立会话或不建立会话.如果会话结束,然后在同一个通道上发送消息,将会引发异常. WCF中的会话机制通过设置服务协定(ServiceContract)上的SessionMode的枚举值