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事業哦