专栏首页技术小讲堂WCF中数据契约之已知类型的几种公开方式代码中定义配置中定义宿主端使用解析器

WCF中数据契约之已知类型的几种公开方式代码中定义配置中定义宿主端使用解析器

WCF中传输的数据不想传统的面向对象编程,它只传递了一些对象的属性,但是自身并不知道自己属于什么对象,所以,他没有子类和父类的概念,因而也就没有Is-a的关系,所以在WCF中,如果想维持这种继承关系,就需要做一些特殊的处理了。

假设有如下定义,

namespace KnownTypeExampleInterface
{
    [DataContract]
    public class Employee
    {
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string Age { get; set; }
    }

    [DataContract]
    public class Manager:Employee
    {
        [DataMember]
        public int OfficeId { get; set; } 
    }

    public interface IHumanResourceService
    {
        List<Employee> GetAllEmployees();
    }
}

这样,在调用端是无法得到manager的OfficeId的,因为在服务定义中并不知道有Manager类的存在。

解决这种问题的有如下几种方法

代码中定义

解决这种问题的一种方法是使用KnownTypeAttribute告诉WCF存在Manager的信息:

[DataContract]
[KnownType(typeof(Manager))]
public class Employee
{
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public string Age { get; set; }
}

这样,在宿主端,会影响到所有的契约与操作,也就是说使用了Employee的服务契约或者操作,最终在契约中都会存在Manager的定义。

但是如果不想Manager暴露给所有的使用Employee的服务,则可以使用ServiceKnownTypeAttribute应用在服务定义或者操作定义上,这样就只会有服务或者操作才能够接受Manager子类了。

public interface IHumanResourceService
{
    List<Employee> GetAllEmployees();
    [ServiceKnownType(typeof(Manager))]
    void AddEmployee(Employee employee);
}

配置中定义

在代码中定义的有一个主要的缺陷,就是客户端必须事先知道这些子类,添加一个子类就得修改一次代码,重新编译,部署,所以WCF也允许允许通过配置文件的方式添加这些子类。

<system.runtime.serialization>
    <dataContractSerializer>
      <declaredTypes>
        <add type="Employee,KnownTypeExampleInterface,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null">
          <knownType type="Manager,KnownTypeExampleInterface,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"/>
        </add>
      </declaredTypes>
    </dataContractSerializer>
  </system.runtime.serialization>

宿主端使用解析器

另外一种清大的方法就是使用数据契约解析器,它能够自动化的去解析这些子类,而不需要手动的去添加标签或者修改配置文件。

实现这种数据契约解析器的方法

在WCF中,存在DataContractResolver类,可以在这个类中提供一个维护了唯一标识符和类型之间的映射关系字典,在序列化这个类型时,需要提供一个唯一的标识符作为键形成键与类型的映射关系,WCF会在反序列化期间提供这些键。参照上文中的数据契约,相对应的解析器定义为:

public abstract class ManagerDataContractResolver:DataContractResolver
{
    private string Namespace
    {
        get { return typeof (Manager).Namespace ?? "global"; }
    }

    private string Name
    {
        get { return typeof (Manager).Name; }
    }


    public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
    {
        if (typeName == this.Name && typeNamespace == this.Namespace)
        {
            return typeof (Manager);
        }
        else
        {
            return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null);
        }
    }

    public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
    {
        if (type == typeof (Manager))
        {
            XmlDictionary dic = new XmlDictionary();
            typeName = dic.Add(this.Name);
            typeNamespace = dic.Add(this.Namespace);
            return true;
        }
        else
        {
            return knownTypeResolver.TryResolveType(type, declaredType, null, out typeName, out typeNamespace);
        }
    }
}

自定义的解析器定义完成,之后需要分别在代理端和宿主端安装解析器,

在ServiceEndpoint中有一个类型为ContractDascription的Contract属性,它是一个操作描述的集合,每一个描述操作描述(OperationDescription)都包含一个类型为IOperationBehavior类型的行为集合,而每一个行为又包含一个DataContractResolver属性,这个属性默认为null,就是在这里,可以设置我们自定义的解析器。

static void Main(string[] args)
{
    ServiceHost host = new ServiceHost(typeof (HumanResourceService));
    foreach (ServiceEndpoint endpoint in host.Description.Endpoints)
    {
        foreach (OperationDescription operation in endpoint.Contract.Operations)
        {
            DataContractSerializerOperationBehavior behavior =
                operation.OperationBehaviors.FirstOrDefault(
                    x => x.GetType() == typeof (DataContractSerializerOperationBehavior)) as DataContractSerializerOperationBehavior;
            behavior.DataContractResolver = new ManagerDataContractResolver();
        }
    }
    host.Open();
    Console.WriteLine("Host Running!");
    Console.ReadKey();
    host.Close();
}

而在代理一端,可以使用同样的方式安装解析器,不在赘述!

好了,今天就到这里了,明天去办居(zan)住证,明天开始我也是天津市合法居民了。希望得到您的推荐与点赞,满足虚荣心之后定会贡献更多给IT事业哦

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • iBatis.Net(4):DataMapper API

    在iBatis.Net中,可以通过SqlMapper实例访问DataMapper API,其实以前写的都是一些iBatis.Net的概念和一些配置的问题,从这一...

    小白哥哥
  • iBatis.Net(5):Data Map(了解)

    总算,总算,能写点示例啦,呵呵,其实前面的几篇,我感觉自己写的也很生硬,没有Demo理解起来是很困难,很多名词,反正我初次接触iBatis的时候,是一点也不理解...

    小白哥哥
  • LINQ to SQL(4):OR设计器

    在我们使用LINQ to SQL的时候,需要大量的使用OR设计器,虽然我们手工写代码也是完全可以实现的,但是OR设计器是非常强大的工具,我想有了它,没有几个人会...

    小白哥哥
  • 【译】尝试使用Nullable Reference Types

    随着.NET Core 3.0 Preview 7的发布,C#8.0已被认为是“功能完整”的。这意味着它们的最大亮点Nullable Reference Typ...

    Edison.Ma
  • emlog文章短网址版权插件

    Youngxj
  • PHP过滤敏感词

    PHP实现的敏感词过滤方法,有好的编码和好的实现方法,可以发出来一起交流一下。以下是一份过滤敏感词的编码

    php007
  • 容器化 TensorFlow GPU 环境搭建

    CPU:Intel Xeon E5-2699 v4 显卡:Nvidia Tesla P100 操作系统:CentOS 7.4

    轻量级微服务
  • 全球最大的基金公司富达投资未来战略:人工智能、区块链和虚拟现实

    拥有72年历史、管理资产高达7万亿美元的富达投资正大举投资人工智能、区块链和虚拟现实技术,每年在技术研发上投资25亿美元,先后建立了富达应用技术中心、富达实验室...

    点滴科技资讯
  • 文字识别刷新世界纪录,海康威视浦世亮新智元“AI春节”解密安防大数据 | 新智元峰会演讲

    【新智元导读】在3月27日举行的中国“AI春节”——2017新智元开源·生态AI技术峰会上,海康威视研究院院长浦世亮发表演讲《安防大数据驱动下的智慧生活》,介绍...

    新智元
  • 网络推广提升百度搜索时网站排名的SEO常用方法

    百度,是我们生活中经常使用到的网络工具,大家在使用百度的时候也是非常看重权重,那么百度权重是什么?又该如何提升呢?今天小编就和大家介绍一下最新百度权重提升方法!

    橙柚青

扫码关注云+社区

领取腾讯云代金券