WCF接口、抽象类等数据契约传递方式

时间:2022-11-23 09:56:26


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; }
}

配置中定义
在代码中定义的有一个主要的缺陷,就是客户端必须事先知道这些子类,添加一个子类就得修改一次代码,重新编译,部署,所以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();
}