WCF足跡2:契約

契約是WCF中很重要的概念。它是用一種與平臺無關的標準語法來描述WCF服務的功能。當客戶端獲取服務端WCF服務的時候,會根據服務端聲明的契約生成客戶端契約的複本,客戶端和服務端通過契約來實現溝通。

一個生活中的例子:
比如KFC,它是一家快餐品牌。假設我想通過加盟的方式在我家附近開一家KFC快餐店。首先,我們要向KFC加盟代理提交加盟申請,經過資格申查後,和KFC籤述加盟協議。然後,我根據協議中規定的條款在我家附開了一家KFC快餐店。KFC總部爲我提供方法、技術和原材料等,我每年向KFC總部交加盟費,這樣附近的朋就可以從我的KFC快餐店中獲得KFC的產品和服務了。
“KFC總部”就相當於我們的WCF服務
“來吃KFC附近的朋友”相當於要獲取WCF服務的客戶端代碼
“我開的KFC快餐店”相當於客戶端的代理類
“我與KFC簽定的加盟協議”相當於WCF的契約。
通過這個例子我們可以看到契約在WCF中的重要性,它就像服務端提供的“加盟協議”一樣,客戶端根據“加盟協議”中規定的要求在客戶端生成代理類(開辦加盟店),並根據加盟協議規定的權利從服務端獲取服務(獲取方法、技術和原材料等),這樣客戶端在我的加盟店裏就可以直接得到KFC服務。
所以說契約是服務端與客戶端進行信息交流的基礎。

在WCF中包括了四種契約:服務契約,數據契約,錯誤契約和消息契約。在這裏我們重點來看服務契約和數據契約。
在WCF中契約是以Attribute型式進行聲明的。

1.用來定義服務契約的兩個Attribute:
[AttributeUsage(AttributeTargets.Interface|AttributeTargets.Class,Inherited = false)]
public sealed class ServiceContractAttribute : Attribute
{
   public string Name
   {get;set;}
   public string Namespace
   {get;set;}
   //More members
}

[AttributeUsage(AttributeTargets.Method)]
public sealed class OperationContractAttribute : Attribute
{
   public string Name
   {get;set;}
   //More members
}

2.用來定義數據契約的兩個Attribute:
[AttributeUsage(AttributeTargets.Enum | AttributeTargets.Struct | AttributeTargets.Class, Inherited = false)]
public sealed class DataContractAttribute : Attribute
{
   public string Name
   {get;set;}
   public string Namespace
   {get;set;}
}

[AttributeUsage(AttributeTargets.Field|AttributeTargets.Property, Inherited = false)]
public sealed class DataMemberAttribute : Attribute
{
   public bool IsRequired
   {get;set;}
   public string Name
   {get;set;}
   public int Order
   {get;set;}
}

一、服務契約:

1.服務契約的概念
我們可以在接口或者類上聲明[ServiceContract]和[OperationContract]來定義服務契約。
[ServiceContract]
interface IMyContract
{
   [OperationContract]
   string MyMethod(string text);

   //MyOtherMethod方法沒有聲明[OperationContract],不會成爲契約的一部份
   string MyOtherMethod(string text);
}
class MyService : IMyContract
{
   public string MyMethod(string text)
   {
      return "Hello " + text;
   }
   public string MyOtherMethod(string text)
   {
      return "Cannot call this method over WCF";
   }
}

ServiceContract聲明用來把.NET中的接口聲明(CLR格式)映射爲與平臺無關的契約聲明(XML格式),以向外界暴露服務訪問入口ServiceContract聲明與類的訪問修飾符無關,即不管接口(類)的訪問修飾符是public/private/protected/internal,只要把該接口(類)聲明爲ServiceContract,該接口(類)總會變成服務契約暴露給客戶端。因爲訪問修飾符(public/private/protected/internal)定義的是在CLR中的訪問邊界,而ServiceContract定義的是在WCF中的訪問邊界。
在WCF中服務契約接口都需要顯示聲明爲ServiceContract,否則,接口不會被當成WCF契約向外界暴露。
即使我們把接口聲明爲ServiceContract了,但該服務契約現在並不包含任何成員,我們還要在需要作爲契約成員的方法上面加上OperationContractAttribute聲明。像上面的代碼中,MyMethod方法會作爲IMyContract契約的成員向外界暴露,而MyOtherMethod方法則不會成爲IMyContract契約的成員。
OperationContract 可以應用在成員方法、屬性、索引器和事件上面

2.ServiceContract的NameSpace屬性和Name屬性
在編寫WCF服務的時候,我們應當爲每個服務契約設置NameSpace屬性,如果爲服務契約指定NameSpace屬性的話,那該服務契約會默認NameSpace="http://tempuri.org"。這裏NameSpace的作用與原來CLR中NameSpace的作用一樣,都是爲了定義一個命名空間,防止命名的衝突
如:
[ServiceContract(Namespace = "http://hi.baidu.com/grayworm")]
interface IMyContract
{...}
對Internet發佈的服務契約,命名空間一般使用公司的網址進行命名,對於局域網內發佈的服務契約則沒有必要按照這種方式進行命名,我們可以使用更有意義單詞作爲NameSpace。
[ServiceContract(Namespace = "MyNamespace")]
interface IMyContract
{...}


我們還可以爲服務契約指定別名。在默認的情況下,服務契約的名稱與接口的名稱一樣,我們可以在ServiceContract聲明中使用Name屬性爲服務契約指定別名。
[ServiceContract(Namespace="http://hi.baidu.com/grayworm",Name="GrayWormCaculate")]
public interface ICaculator
{
    [OperationContract(Name="AddInt")]
    int Add(int arg1, int arg2);
    [OperationContract(Name="AddDouble")]
    double Add(double arg1, double arg2);
}
測試結果:


《圖2》
從圖中我們可以看出服務的名子不再是接口的名子了。

3.服務契約中的方法重載
在面向對象的思想中,我們有方法重載的概念,所謂的方法重載就是指一個類中如果兩個方法的方法名相同而方法參數不同,那這兩個參數就形成了重載
如:
interface ICalculator
{
   int Add(int arg1,int arg2);
   double Add(double arg1,double arg2);
}

CLR可以根據方法的能數來區分這兩個方法。而在WCF世界中這種方法名相同而參數不同的形式則會引發InvalidOperationException異常,即在WCF中不支持面向對象中的方法重載。
如:
//這是種契約定義是錯誤的
[ServiceContract]
interface ICalculator
{
   [OperationContract]
   int Add(int arg1,int arg2);

   [OperationContract]
   double Add(double arg1,double arg2);
}
上面這個服務契約編譯的時候是沒有問題的,因爲它符合CLR的重載要求,但當我們使用HOST發佈服務的時候會產生下面的問題:


《圖1》

下面我們看一下如何解決ClR和WSDL中不統一的情況:
我們可以在OperationContract聲明上通過Name屬性爲方法起別名,將來客戶端就會通過這個別名來區分不同方法的。如:
[ServiceContract]
public interface ICaculator
{
    [OperationContract(Name="AddInt")]
    int Add(int arg1, int arg2);
    [OperationContract(Name="AddDouble")]
    double Add(double arg1, double arg2);
}
這樣在客戶端會把兩個Add方法區分爲AddInt和AddDouble兩個方法。測試結果如下圖:


《圖3》

4.服務契約的繼承
下面我們看一下契約的繼承。
我先聲明一個簡單計算器的契約ISimpleCaculator,只能夠作加法運算:
[ServiceContract]
public interface ISimpleCaculator
{
    [OperationContract]
    int Add(int arg1, int arg2);
}

我們再聲明一個科學計算器的契約IScientificCaculator,派生自ISimpleCaculator,在簡單計算器的功能之上還能夠做乘法運算。
[ServiceContract]
public interface IScientificCaculator:ISimpleCaculator
{
    [OperationContract]
    int Mutiply(int arg1, int arg2);
}

然後我們再編寫一個類實現IScientificCaculator接口。
public class MyCaculator : IScientificCaculator
{
    public int Add(int arg1, int arg2)
    {
        return arg1 + arg2;
    }
    public int Mutiply(int arg1, int arg2)
    {
        return arg1 * arg2;
    }
}

服務契約編寫完成後,我們再在宿主程序中配置終結點:
<service name = "MyCalculator">
   <endpoint
      address = "
http://localhost:8001/MyCalculator/"
      binding = "basicHttpBinding"
      contract = "IScientificCalculator"
   />
</service>

這樣我們就把WCF服務和宿主程序編寫好了,下一步就在客戶端添加WCF服務的引用。當添加完對WCF服務的引用後,客戶端就會通過元數據終結點獲取服務契約的信息,並在客戶端生成代理代。
代理類的契約聲明代碼如下:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="SR.IScientificCaculator")]
public interface IScientificCaculator {
   
    [System.ServiceModel.OperationContractAttribute(Action="
http://tempuri.org/ISimpleCaculator/Add", ReplyAction="http://tempuri.org/ISimpleCaculator/AddResponse")]
    int Add(int arg1, int arg2);
   
    [System.ServiceModel.OperationContractAttribute(Action="
http://tempuri.org/IScientificCaculator/Mutiply", ReplyAction="http://tempuri.org/IScientificCaculator/MutiplyResponse")]
    int Mutiply(int arg1, int arg2);
}
從上面的代碼中我們看出,雖然在服務端我們編寫了兩個有繼承關係的契約,但在客戶端並沒有爲我們生成兩個對應的契約,而是隻生成了一個服務契約在這個服務契約中包含了兩個OperationContract。
這兩個OperationContract分別與服務端兩個ServiceContract中的OperationContract相對應。由於這兩個OperationContract來源於不同的服務契約,所以在OperationContract的屬性中有Action="http://tempuri.org/ISimpleCaculator/Add"和Action="http://tempuri.org/IScientificCaculator/Mutiply"兩個屬性聲明,這個Action屬性就是映射該OperationContract服務端的OperationContract。

二、數據契約
1.數據契約的概念。
我們在進行WCF編程的時候,服務端程序難免會與客戶端程序之間發生數據交換,由於服務端與客戶端可能是兩種異質運行環境,這就需要實現服務端的數據類型與客戶端代理類數據類型的統一。
在服務器端與客戶端交換數據是通過流來實現的,因此在傳遞對象的時候需要我們把對象轉換到流中去,在目的地我們再從流中把數據讀取出來重新生成能相應對象,這個思想就是我們序列化的思想。在DotNET序列化中是通過Serialization聲明來標識類允許被實例化的,這種序列化只是把數據序列化到流中去,而在WCF中不僅僅要把數據序列化到流中去還應包含數據類型的描述。因爲客戶端的程序可能與服務器端的程序不樣而無法實現數據準確的序列化和反序列化,比如服務器端我是用WCF開發的,而客戶端是用JavaEE開發的,現在需要從服務器端返回一個Dog對象給客戶端。如果只使用簡單的序列化和反序列化的話,可能會產生問題:服務器端DotNET序列化的數據在客戶端JavaEE不能識別流的格式,無法實現返序列化。
數據契約的作用就是實現一種與平臺無關的序列化,即在序列化過程中實現在schema與CLR類型之間轉換。
許多內置類型都默認可以被序列化,但自定義類型我們就需要使用數據契約來顯式指明其可被序列化。

數據契約使用DataContract和DataMember來聲明。
DataContract:修飾可被序列化的數據類型。
DataMember:修飾可被序列化的成員,可以修飾成員變量也可以修飾屬性。

[DataContract]
struct Contact
{
   [DataMember]
   public string FirstName;

   [DataMember]
   public string LastName;
}
或者
[DataContract]
struct Contact
{
   string m_FirstName;
   string m_LastName;

   [DataMember]
   public string FirstName
   {
      get
      {...}
      set
      {...}
    }

   [DataMember]
   public string LastName
   {
      get
      {...}
      set
      {...}
    }
}

與服務契約一樣,使用DataContract和DataMember聲明的數據契約也與訪問修飾符(public,private,protected...)無關。

2.數據契約的傳遞
a.命名空間
服務器端定義了一個數據契約,客戶端在生成代理類的時候也會生成一個對等的數據契約的複本,但這個契約的複本和服務器端的契約還是有稍許的不同,但命名空間默認是一樣的。
如:
服務器端的數據契約定義
namespace MyNamespace
{
   [DataContract]
   struct Contact
   {...}

   [ServiceContract]
   interface IContactManager
   {
      [OperationContract]
      void AddContact(Contact contact);

      [OperationContract]
      Contact[] GetContacts( );
   }
}

傳遞到客戶端的數據契約複本爲:
namespace MyNamespace
{
   [DataContract]
   struct Contact
   {...}
}
[ServiceContract]
interface IContactManager
{
   [OperationContract]
   void AddContact(Contact contact);

   [OperationContract]
   Contact[] GetContacts( );
}

如果要想改變傳遞到客戶端的數據契約的命名空間,我們可以在服務器端數據契約聲明的時候加上NameSpace屬性
namespace MyNamespace
{
   [DataContract(Namespace = "MyOtherNamespace")]
   struct Contact
   {...}
}

這樣傳遞到客戶端的數據契約就會變爲
namespace MyOtherNamespace
{
   [DataContract]
   struct Contact
   {...}
}

b.DataMember聲明的使用
DataMember聲明可以加在成員變量上,也可以加在屬性上。不管怎樣使用DataMember聲明,總會在客戶端代理類中生成帶有DataMember聲明的相關屬性。

服務端DataContract成員變量加上DataMember聲明的時候,客戶端代理類會產生對應的DataMember聲明的屬性。客戶端代理類DataMember屬性的名子與服務端DataMember成員變量名子相同,並在客戶端動態生成對應的成員變量,成員變量命名是在DataMember屬性名子的後面加Feild的形式。

如服務端數據契約的聲明如下:
[DataContract]
public class Book
{
   [DataMember]
    public string BookNO;
   [DataMember]
    public string BookName;
   [DataMember]
    public decimal BookPrice;
}

客戶端生成代理類中數據契約的聲明如下:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="Book", Namespace="
http://schemas.datacontract.org/2004/07/Services")]
[System.SerializableAttribute()]
public partial class Book : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
   
    [System.Runtime.Serialization.OptionalFieldAttribute()]
    private string BookNOField;
   
    [System.Runtime.Serialization.OptionalFieldAttribute()]
    private string BookNameField;
   
    [System.Runtime.Serialization.OptionalFieldAttribute()]
    private decimal BookPriceField;
   
   [System.Runtime.Serialization.DataMemberAttribute()]
    public string BookNO {
        get {...}
        set {...}
        }
    }
   
   [System.Runtime.Serialization.DataMemberAttribute()]
    public string BookName {
        get {...}
        set {...}
        }
    }
   
   [System.Runtime.Serialization.DataMemberAttribute()]
    public decimal BookPrice {
        get {...}
        set {...}
        }
    }
    //其它的屬性和方法  
}

當服務端DataContract屬性上加上DataMember聲明的時候,客戶端代理類會產生對應的DataMember聲明的屬性。客戶端代理類DataMember屬性的名子與服務端DataMember屬性名子相同,並在客戶端動態生成對應的成員變量,成員變量命名是在屬性名子的後面加Feild的形式。

如服務端數據契約的聲明如下:
[DataContract]
public class Book
{
    private string _BookNO;
   [DataMember]
    public string BookNO
    {
        get { return _BookNO; }
        set { _BookNO = value; }
    }
    private string _BookName;
   [DataMember]
    public string BookName
    {
        get { return _BookName; }
        set { _BookName = value; }
    }
    private decimal _BookPrice;
   [DataMember]
    public decimal BookPrice
    {
        get { return _BookPrice; }
        set { _BookPrice = value; }
    }
}

客戶端生成代理類中數據契約的聲明如下:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="Book", Namespace="
http://schemas.datacontract.org/2004/07/Services")]
[System.SerializableAttribute()]
public partial class Book : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
   
    [System.Runtime.Serialization.OptionalFieldAttribute()]
    private string BookNOField;
   
    [System.Runtime.Serialization.OptionalFieldAttribute()]
    private string BookNameField;
   
    [System.Runtime.Serialization.OptionalFieldAttribute()]
    private decimal BookPriceField;
   
    [System.Runtime.Serialization.DataMemberAttribute()]
    public string BookNO {
        get {...}
        set {...}
        }
    }
   
   [System.Runtime.Serialization.DataMemberAttribute()]
    public string BookName {
        get {...}
        set {...}
        }
    }
   
    [System.Runtime.Serialization.DataMemberAttribute()]
    public decimal BookPrice {
        get {...}
        set {...}
        }
    }
    //其它的屬性和方法  
}

凡是被DataMember聲明修飾的屬性,必須要有get和set訪問器,如果沒有這兩個訪問器在調用的時候會產生InvalidDataContractException異常信息,因爲在序列化的過程中需要通過get和set訪問器來操作對象的數據。
要注意的是不要在成員變量和對應屬性上都加上DataMember聲明,這樣會在客戶端代理類中產生重複的成員。

3.數據契約的繼承
如果兩個數據契約類之間有繼承關係,需要在這兩個類上面都加上DataContract聲明,因爲DataContract不能被繼承。
如服務端的數據契約聲明如下:
[DataContract]
public class Pet
{
    private string _Name;
   [DataMember]
    public string Name
    {
        get { return _Name; }
        set { _Name = value; }
    }
    private string _Owner;
   [DataMember]
    public string Owner
    {
        get { return _Owner; }
        set { _Owner = value; }
    }
}
[DataContract]
public class Dog:Pet
{
    private string _Race;
    [DataMember]
    public string Race
    {
        get { return _Race; }
        set { _Race = value; }
    }
}
如果Dog類中沒有聲明[DataContract]時,在服務加載運行的時候就會產生InvalidDataContractException異常信息。

4.已知數據類型
在C#面向對象的語法中,可以使用子類對象來替代父類對象,但在WCF中則不允許使用這種替代。

如服務契約PetShop返回Dog數據契約來替代Pet數據契約。
[ServiceContract]
public interface IPetSop
{
    [OperationContract]
    Pet Sell(string owner,string race);
}
public class PetShop : IPetSop
{
    public Pet Sell(string owner,string race)
    {
        Dog dog = new Dog();
        dog.Name = Guid.NewGuid().ToString();
        dog.Owner = owner;
        dog.Race = race;
        return dog;
    }
}
在面向對象語法中這種寫法完全正確,但在面向服務的語法中這種寫法是錯誤的,在客戶端調用的時候會產生如下錯誤:


《圖4》

在WCF中爲什麼會產生這種怪異的顯象呢?因爲在WCF服務中產生的對象並不是直接被客戶端進行使用的,而是根據數據契約,先把服務端產生的對象序列化,然後通過信道發送給客戶端,在客戶端再根據契約反序列化對象然後再獲取數據。在這個例子中,在服務器端序列化的Pet對象是Dog對象,而在客戶端卻只瞭解Pet數據契約,不知道Dog數據契約,所以在反序列化的時候會產生異常。
這個問題不僅僅是個返回類型或參數類型的問題,而是涉及到WCF數據契約究竟能否實現“多態性”的問題。爲了解決這個問題,我們引入了“已知數據類型屬性”這個概念。“已知數據類型屬性”就是在父類中註冊一下派生自它的子類。

《天龍八部》節選:
......
段延慶冷笑道:“順我者昌,逆我者亡”!提起鋼杖,便向段譽胸口戳了下去。
忽聽得一個女子的聲音說到:“天龍寺外,菩提樹下,化學邋遢,觀音長髮!”
段延慶聽到“天龍寺外”四字時,鋼杖凝在半空不動,待聽完這四句話,那鋼杖竟不住顫動,慢慢縮了回來。他一回頭,與刀白鳳的目光相對,只見她眼色中似有千言萬語欲待吐露。段延慶心頭大震,顫聲道:“觀……觀世音菩薩……”
刀白鳳點了點頭,低聲道:“你……你可知這孩子是誰?”
段延慶腦子中一陣暈眩,瞧出來一片模糊,似乎是回到了二十多年前的一個月圓之夜......
......
看過《天龍八部》的朋友應當對這個情節不陌生。這個例子雖然有些“少兒不宜”,但我感常見它能夠比較好地解釋“已知數據類型”的作用。這個例子中,刀白鳳的那四句話,就是向父類(段延慶)註冊了一個子類(段譽)爲已知數據類型。

a.在父類數據契約上使用KnownTypeAttribute
“已知數據類型”可以在數據契約的父類上加上[KnownType(Type t)]這個Attribute來實現的。代碼如下:
[DataContract]
[KnownType(typeof(Dog))]
public class Pet
{
    ...
}
[DataContract]
public class Dog:Pet
{
    ...
}

運行效果:


《圖5》

這是因爲在客戶端生成代理類的時候,會根據[KnownType(typeof(Dog))]聲明在客戶端同時生成Pet契約和Dog契約,這樣客戶端可以識點Dog對象,能對其反序列化。


《圖6》

b.在父類數據契約的方法中使用ServiceKnownTypeAttribute
上面使用KnownTypeAttribute可以解決子類對象替代父類對象的問題,但它是定義在數據契約類的級別上的,所定義的範疇有些大。
這裏我們可以使用ServiceKnownTypeAttribute。ServiceKnownTypeAttribute是定義在服務契約的方法契約級別上的,只有當前方法契約可以識別子類Dog,其它方法中無法識別Dog。
[ServiceContract]
public interface IPetSop
{
    [OperationContract]
    [ServiceKnownType(typeof(Dog))]
    Pet Sell(string owner,string race);
}
public class PetShop : IPetSop
{
    public Pet Sell(string owner,string race)
    {
        Dog dog = new Dog();
        dog.Name = Guid.NewGuid().ToString();
        dog.Owner = owner;
        dog.Race = race;
        return dog;
    }
}
當[ServiceKnownType(Type t)]被聲明在服務契約的級別上時,當前服務契約中的任何方法都可以識別它所指定的子類。
[ServiceContract]
[ServiceKnownType(typeof(Dog))]
public interface IPetSop
{
    [OperationContract]
    Pet Sell(string owner,string race);
}
public class PetShop : IPetSop
{
    public Pet Sell(string owner,string race)
    {
        Dog dog = new Dog();
        dog.Name = Guid.NewGuid().ToString();
        dog.Owner = owner;
        dog.Race = race;
        return dog;
    }
}
注:
1.不要把ServiceKnowntypeAttribute聲明加在服務類本身上,但可以把ServiceKnowntypeAttribute聲明加在接口服務契約中。
2.KnownTypeAttribute(Type t)是用在數據契約中的,而ServiceKnowntypeAttribute(Type t)是用在服務契約中的。

c.使用多個“已知數據類型”
我們可以使用多個KnownTypeAttribute或ServiceKnowntypeAttribute來告訴WCF識別多個子類
服務端代碼:
[DataContract]
class Contact
{...}

[DataContract]
class Customer : Contact
{...}

[DataContract]
class Person : Contact
{...}

[ServiceContract]
[ServiceKnownType(typeof(Customer))]
[ServiceKnownType(typeof(Person))]
interface IContactManager
{...}
在這個例子中有兩層繼承關係,在服務契約IContactManager上我們需要把兩級子類都聲明爲“已知數據類型”,否則會產生異常。

4.使用配置文件指定“已知數據類型”
上面我們可以爲WCF服務設置“已知數據類型”,但是當需要把一個新的子類添加爲“已知數據類型”時,就需要我們對現有WCF服務進行修改源代碼、重新編譯、重新佈署等操作,爲我們服務的可擴展性大打折扣。爲了避免這種問題的出現,WCF允許我們把“已知數據類型”配置在宿主程序的配置文件中。
配置代碼如下:
<configuration>
    <system.runtime.serialization>
        <dataContractSerializer>
            <declaredTypes>
                <add type = "Services.Pet,Services">
                    <knownType type = "Services.Dog,Services"/>
                </add>
            </declaredTypes>
        </dataContractSerializer>
    </system.runtime.serialization>
這樣在客戶端生成代理類的時候會根據宿主程序上面的配置文件,在客戶端代理類中生成對應的Pet數據契約複本和Dog數據契約複本,並在Pet數據契約複本上加上了KnownTypeAttribute聲明。

5.枚舉類型
枚舉類型默認會自動被序列化,枚舉類型的值也會自動被包含在數據契約中,所以沒有必要在枚舉類型上加DataContractAttribute,直接在服務契約中使用就可以了。
在服務契約中使用枚舉類型:
enum ContactType
{
   Customer,
   Vendor,
   Partner
}

[DataContract]
struct Contact
{
   [DataMember]
   public ContactType ContactType;

   [DataMember]
   public string FirstName;

   [DataMember]
   public string LastName;
}

當然我們也可以顯式地把枚舉類型聲明爲數據契約,首先需要在枚舉上聲明DataContractAttribute,然後再在枚舉值上加上EnumMemberAttribute 聲明,而沒明顯示聲明爲EnumMemberAttribute的枚舉值將不會包含在該數據契約中。
如:
[DataContract]
enum ContactType
{
[EnumMember(Value = "MyCustomer")]
   Customer,

   [EnumMember]
   Vendor,

   //由於沒加EnumMemberAttribute聲明,Partner不會是數據契約中的成員
   Partner
}
在客戶端生成的代理類的數據契約聲明如下:
enum ContactType
{
   MyCustomer,
   Vendor
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章