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 条评论
登录 后参与评论

相关文章

来自专栏https://www.cnblogs.com/L

【Linux篇】--awk的使用

awk是一个强大的文本分析工具。相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。 简单来说awk就是把文件逐行的读入,(空格...

1152
来自专栏名山丶深处

springboot集成jpa

2165
来自专栏名山丶深处

springboot集成jpa

1437
来自专栏Python攻城狮

Django教程(三)- Django表单Form1.Form 基本使用2.Form中字段及插件3.通过Django表单Form来完成需求4.自定义验证验证规则

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;

1164
来自专栏java一日一条

2015年Java开发岗位面试题归类

3. 说说你知道的几个Java集合类:list、set、queue、map实现类咯。。。

611
来自专栏Unity

检测Unity客户端无效的预设资源

4、不被代码间接引用,如预设Anniver_01,在代码中是"Anniver_"+number,这种暂时没有好办法解决。

1455
来自专栏Charlie's Road

<Solidity学习系列四>使用编译器

Solidity存储库的一个构建目标是solc,solidity命令行编译器。 使用solc --help为您提供所有选项的解释。 编译器可以生成各种输出,范围...

852
来自专栏逸鹏说道

C#进阶系列——WebApi 接口参数不再困惑:传参详解上

前言:还记得刚使用WebApi那会儿,被它的传参机制折腾了好久,查阅了半天资料。如今,使用WebApi也有段时间了,今天就记录下API接口传参的一些方式方法,算...

3618
来自专栏LinXunFeng的专栏

打造Moya便捷解析库,提供RxSwift拓展

1、相信大家在使用Swift开发时,Moya是首选的网络工具,在模型解析这一块,Swift版模型解析的相关第三方库有很多,本人最习惯用的就是SwiftyJSON...

20711
来自专栏青玉伏案

iOS逆向工程之Hopper中的ARM指令

虽然前段时间ARM被日本软银收购了,但是科技是无国界的,所以呢ARM相关知识该学的学。现在看ARM指令集还是倍感亲切的,毕竟大学里开了ARM这门课,并且做了不少...

2367

扫码关注云+社区