• 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();
                }
            }
        }
    }
    View Code

      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>
    View Code

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

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

    • 在运行中输入cmd打开命令行,输入 cd C:Program Files (x86)Microsoft SDKsWindowsv7.0ABin
    • 输入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来告知客户端需要反序列化子类的成员。
  • 相关阅读:
    【转】框架集中framespacing、border和frameborder属性的关系
    使用ArcGIS GP服务之二手工建模
    使用ArcGIS GP服务之五 JavaScript的调用
    使用ArcGIS GP服务之三发布前的准备
    计算GPS WGS_84 两点的距离
    使用ArcGIS GP服务之四GP服务发布
    计算GPS WGS_84 两点的距离 更加细腻的算法
    QueryPerformanceFrequency
    cocos2dx App 图标
    cocos2dx CCTimer::timerWithTarget回调
  • 原文地址:https://www.cnblogs.com/linybo/p/13306333.html
Copyright © 2020-2023  润新知