WCF proxy 類別的屬性名稱全都多加了 "Field"

同樣是呼叫第三方 web service 時碰到的狀況:Visual Studio 產生的 proxy 類別的每個屬性名稱後面都多加了 "Field",例如:CustomerIdField、CompanyField 等等。先說解法:在定義 WCF 服務的介面時,為每個介面方法套用 XmlSerializeFormatAttribute 就行了。

(以下是細節描述,純粹記錄,除非你也碰到同樣問題,否則無須細讀。)


問題:Visual Studio 產生的 WCF 用戶端 proxy 類別的屬性名稱會多加 "Field"。

得先說一下,之所以會出現這種狀況,是因為有兩層 web services,像這樣:

WCF 用戶端 app ----------> MyWrapperService -----------> 第三方 Java web service

基於某些原因,用戶端 app 不能直接呼叫第三方 web service,而得透過中間的 MyWrapperService 來轉呼叫。MyWrapperService 是個 WCF service,單純作為轉接器,是第三方 web service 的拷貝。

也就是說,在 MyWrapperService 專案中,除了定義自己的服務介面供用戶端呼叫,還會有一個對應至第三方 web service 的 proxy 類別。基本上,MyWrapperService 幾乎完全複製了第三方 web service 的服務介面,其中包括用來當作傳入參數或傳回值的自訂複雜型別也都一樣。

還是補張圖,比較清楚:


在 MyWrapperService 專案中,查看 Visual Studio(SvcUtilexe)為第三方 web service 產生的 proxy 類別的原始碼(Reference.cs),可以發現其私有欄位名稱都會額外加上 "Field" 字樣,例如 customerIdField,公開屬性的名稱則維持原樣,例如 customerId。底下是個範例:

public partial class GetCustomerResponse : object, System.ComponentModel.INotifyPropertyChanged 
{
    private int customerIdField;

    [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
    public int customerId 
    {
        get {
            return this.customerIdField;
        }
        set {
            this.customerIdField = value;
            this.RaisePropertyChanged("customerId");
        }
    }
}

這沒問題,只要公開屬性的名稱與原本 web service 中的屬性名稱一樣就好。

然後,在 MyWrapperService 中定義服務介面,這個介面會與第三方 web service 的介面長得很像:

[ServiceContract]
public interface IMyWrapperService
{
    [OperationContract]
    Party3.Services.GetCustomerResponse GetCustomer(int id);
}

其中的 Party3.Services.GetCustomerResponse 型別就是上一個範例中的那個第三方 web service 的 proxy 類別中的一個回傳型別。

中間的轉接器寫完了,接著要寫用戶端程式。然而在用戶端應用程式專案中使用 Visual Studio 的「Add Service Reference」來產生 MyWrapperService 的 proxy 類別時,卻發現產生出來的 proxy 類別裡面的屬性名稱都會多加上 "Field",而屬性所對應的私有欄位名稱則是變成加倍的 "FieldField"。例如前面的 GetCustomerResponse 型別,經由工具產生對應的 proxy 之後,會變成這樣:

public partial class GetCustomerResponse : object, System.ComponentModel.INotifyPropertyChanged 
{
    private int customerIdFieldField; // 私有欄位名稱變成 "xxFieldField"

    [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
    public int customerIdField // 公開屬性名稱額外加了 "Field"
    {
        get {
            return this.customerIdFieldField;
        }
        set {
            this.customerIdFieldField = value;
            this.RaisePropertyChanged("customerIdField");
        }
    }
}

如此一來,在撰寫用戶端程式時,與該服務相關的所有型別的公開屬性名稱後面就得加上額外的 "Field",這已經和原始 web service 中定義的屬性名稱不同,雖然可以編譯,也可以執行,但看著實在彆扭。

原因:ServiceModel Metadata Utility (SvcUtil.exe) 工具的預設行為(這表示其實我不知道真正原因 XD)。

解法:在欲呼叫的目標 WCF 服務的介面中為每個方法套用 [XmlSerializeFormatAttribute]。

像這樣:

[ServiceContract]
public interface IMyWrapperService
{
    [XmlSerializerFormat]
    [OperationContract]
    GetCustomerResponse GetCustomer(int id);
}

就行了。

參考資料

沒有留言:

技術提供:Blogger.
回頂端⬆️