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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM