1. 程式人生 > >WCF-WCF中傳輸泛型List 物件(轉)

WCF-WCF中傳輸泛型List 物件(轉)

public partial class _Default : System.Web.UI.Page 
{
    
protected void Page_Load(object sender, EventArgs e)
    {
        ServiceReference1.ServiceClient prox 
= new ServiceReference1.ServiceClient();

        
/*********** List<string> ***********///string[] list1 = new string[2];   //未改設定前,Server 返回的 List<string>,Client 只能取得 string 陣列
        List<string> list1 = new List<string>();        
        list1 
= prox.getListString();

        Response.Write(list1[
0+ "<br>");
        Response.Write(list1[
1+ "<p>");


        
/*********** List<自定義類> ***********/
        List
<ServiceReference1.Employee> list2 
= new List<ServiceReference1.Employee>();
        list2 
= prox.getListEmployee();
        
   在程式設計時,DataSet、DataTable,以及 List、Dictionary 等 Collection 型別常會使用到。在 .NET Web Service 和 WCF 中,伺服器端函式 (Operation) 的返回型別,若為 DataSet、DataTable,則客戶端可直接呼叫 (若客戶端程式也是 .NET 的話);但在 WCF 中,VS 2008 預設的配置,並無法傳輸 List<string>、List<自定義類> 等型別的物件,而泛型的 Dictionary 物件卻可以。
[注:Method、Operation 中文都叫做「方法」,但前者是存在 OO 中的類,不存在網路上;後者存在於 Service 中,公開在網路上可供其他程式呼叫。WCF、Data Services 和 RIA Services 中公開在網路上的函式和方法,都可稱作 Operation。]


關於這點,小弟我查了微軟 MCTS 認證 WCF 3.5 的官方用書 [10]、O'Reilly 的書籍 [11],都未提到如何解決,書中只提到 .NET collections 的 metadata,以 WSDL 在網路上傳輸時,會以「陣列 (array)」的格式呈現。


      Because .NET collections are .NET-specific, WCF cannot expose them in the service metadata, yet because they are so useful, WCF offers dedicated marshaling rules for collections.


Whenever you define a service operation that uses the collection interfaces IEnumerable<T>, IList<T>, or ICollection<T>, the specific collection-type information gets lost in the metadata (WSDL) export, so in terms of how collection types are sent across the wire, they all are represented as arrays, the resulting metadata always uses an array.


開發 WCF 時,若 VS 2008 都用預設配置,則當 WCF 的伺服器端函式 (Operation) 的返回型別為 List<string> 時,實際返回的型別為 string[] 陣列,因此客戶端若仍用 List<string> 的變數去接收和賦值時,在編譯時期,即會發生下圖 1 的轉型錯誤:




圖 1 List 資料結構反序列化後,在客戶端自動變成了陣列




WCF 客戶端程式「新增服務引用 (Add Service Reference)」的設定即可處理此種需求。做法如下:


請參閱本帖。當我們的客戶端程式,要引用網路上既有的 WCF 服務契約時,我們會如下圖 2 般,新增一個 service proxy reference。




圖 2 在 ASP.NET 客戶端程式中引用 WCF Service 




在下圖 3 的「新增服務引用」窗體中,右上方的「前往」按鈕,是要檢視網路上某個 IP 和埠的 WCF Service;右邊的「發現」按鈕,是要檢視和此客戶端專案,位於同一個 VS 2008 解決方案裡的 WCF Service。此時我們單擊窗體左下方的「高階」按鈕。




圖 3 在此窗格里輸入正確的元資料交換地址,會自動取得 WCF Service 的 Operation 名稱




如下圖 4,我們在「集合型別」下拉選單中,把預設的 System.Array 改成我們想使用的 Generic.List 型別;而另一個「字典集合型別」下拉選單則保持不變,表示此 WCF Service 可在網路上傳輸泛型的 Dictionary 型別物件。




圖 4 預設的集合型別為 System.Array




微軟的 VS 預設會這樣設定,可能如同博文所提到的,WCF 的客戶端可能是舊版 .NET 1.x 版的環境,也可能是 Java 或其他各種非微軟的技術平臺,因此 VS 2008 預設選用所有廠商、所有平臺都支援的 Array 陣列,作為網路傳輸的型別,而非最新版 .NET 平臺特有的 Collection 資料結構。


最後,若使用者端程式要再更改配置,只要如下圖 5 般,在 VS 專案裡既有的 Reference 上,選擇「配置服務引用」即可。




圖 5 在 ASP.NET 客戶端程式中,修改已引用的 WCF Service




以下為本帖下載示例的程式碼。我們在伺服器端的 WCF Service,提供三個返回型別分別為 List<string>、List<自定義類>、Dictionary<string,string> 的函式,給 WCF 客戶端 ASP.NET 程式呼叫,執行結果如下圖 6。


Server-side/IService.cs System.Collections.Generic;
using System.ServiceModel;
using System.Runtime.Serialization;


[ServiceContract]
[ServiceKnownType(typeof(Employee))]
public interface IService
{
    [OperationContract]
    string GetData(int value);


    [OperationContract]
    List<string> getListString();


    [OperationContract]
    [ServiceKnownType(typeof(Employee))]
    List<Employee> getListEmployee();


    [OperationContract]
    Dictionary<string, string> getDictionaryString();
}


[DataContract]
[KnownType(typeof(Employee))]
public class Employee
{
    public Employee(string name, int age, object oooo)
    {
        this.name = name;
        this.age = age;
        this.oooo = oooo;
    }


    [DataMember]
    public string name;
    [DataMember]
    public int age;
    [DataMember]
    public object oooo;
}
Server-side/Service.cs System.Collections.Generic;
using System.Runtime.Serialization;


public class Service : IService
{
    public string GetData(int value)
    {
        return string.Format("You entered: {0}", value);
    }


    public List<string> getListString()
    {
        List<string> list1 = new List<string>();
        list1.Add("List string 元素一");
        list1.Add("List string 元素二");
        return list1;
    }


    public List<Employee> getListEmployee()
    {
        List<Employee> list2 = new List<Employee>();
        list2.Add(new Employee("吳宇澤", 18, new object()));
        list2.Add(new Employee("王大同", 20, new object()));
        return list2;
    }


    public Dictionary<string, string> getDictionaryString()
    {
        Dictionary<string, string> dict1 =  new Dictionary<string, string>();
        dict1.Add("吳宇澤", "程式設計師");
        dict1.Add("王大同", "業務員");
        return dict1;
    }
}


Client-side/Default.aspx.cs


public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        ServiceReference1.ServiceClient prox = new ServiceReference1.ServiceClient();


        /*********** List<string> ***********/
        //string[] list1 = new string[2];   //未改設定前,Server 返回的 List<string>,Client 只能取得 string 陣列
        List<string> list1 = new List<string>();        
        list1 = prox.getListString();


        Response.Write(list1[0] + "<br>");
        Response.Write(list1[1] + "<p>");




        /*********** List<自定義類> ***********/
        List<ServiceReference1.Employee> list2 = new List<ServiceReference1.Employee>();
        list2 = prox.getListEmployee();
        
        Response.Write(list2[0].name + "<br>");
        Response.Write(list2[0].age + "<br>");
        Response.Write(list2[0].oooo + "<p>");      //object 型別




        /*********** Dictionary<string,string> ***********/
        Dictionary<string, string> dict1 = new Dictionary<string, string>();
        dict1 = prox.getDictionaryString();


        foreach (KeyValuePair<string, string> kvp in dict1)
        {
            Response.Write(kvp.Key + ", " + kvp.Value + "<br>");
        }
    }
}


圖 6 本帖示例執行結果,從 WCF Service 返回 List<string>、List<自定義類>、泛型 Dictionary 三種類型的變數
        Response.Write(list2[
0].age + "<br>");
        Response.Write(list2[
0].oooo + "<p>");      //object 型別


        
/*********** Dictionary<string,string> ***********/
        Dictionary
<stringstring> dict1 = new Dictionary<stringstring>();
        dict1 
= prox.getDictionaryString();

        
foreach (KeyValuePair<stringstring> kvp in dict1)
        {
            Response.Write(kvp.Key 
+ "" + kvp.Value + "<br>");
        }
    }
}