2012-12-27 30 views
3

好吧,我在解決特定問題時遇到了很多困難。通過服務傳輸一個對象。從概念上講,這是有道理的......我想?從我讀過的內容來看,一個Generic不能被序列化,除非它被明確定義。WCF泛型字典和理解WCF

所以我想提供我的例子;我根本無法工作。意思是;我相信也有其他人也遇到一些困難。如果你能提供代碼,當你協助時;這將工作並解釋它。這樣我就可以完全理解這個問題。這將有助於我理解Windows Communication Foundation。

目標是客戶端應用程序,它只有五個字段;在其中'發佈'到服務器。

  • 郵箱地址
  • 電話號碼
  • 網站地址

這是不是太複雜。

這是我所做的,在我學習WCF的過程中,我已經儘可能地接近OOP Principals,因爲我可以爲基於SOA的應用程序提供這些內容。這樣它就提供了代碼重用性。

型號/數據契約:

#region Using Reference... 

using System.Runtime.Serialization; 
using System.Collections.Generic; 
using System.Threading.Tasks; 
using System.Text; 
using System.Linq; 
using System; 

#endregion 

namespace _2Do.Model.Customer 
{ 

    [DataContract(IsReference = true)] 
    public class Person 
    { 

     #region Declared Variable. 

     string first; 
     string last; 
     string email; 
     string phone; 
     string site; 

     #endregion 

     #region Constructor: 

     Person() 
     { 

      // Empty Constructor. 

     } 

     #endregion 

     #region Data Member Properties: 

     [DataMember()] 
     public string First 
     { 

      get { return first; } 
      set { first = value; } 

     } 

     [DataMember()] 
     public string Last 
     { 

      get { return last; } 
      set { last = value; } 

     } 

     [DataMember()] 
     public string Email 
     { 

      get { return email; } 
      set { email = value; } 

     } 

     [DataMember()] 
     public string Phone 
     { 

      get { return phone; } 
      set { phone = value; } 

     } 

     [DataMember()] 
     public string Site 
     { 

      get { return site; } 
      set { site = value; } 

     } 

     #endregion 

    } 

} 

所以這是該對象在其應該通過的元數據客戶端被暴露;所以對於我嘗試的服務接口:

#region Using Reference... 

using System.Collections.Generic; 
using System.Threading.Tasks; 
using System.ServiceModel; 
using _2Do.Model.Customer; 
using System.Text; 
using System.Linq; 
using System; 

#endregion 

namespace _2Do.Contract.Customer 
{ 

    [ServiceContract (Namespace = "https://_2Do") ] 
    public interface IPerson 
    { 

     [OperationContract()] 
     Person SetCustomer(Dictionary<Guid, Person> info); 

    } 

} 

因此,以上是目標;轉移我的Person對象;存儲到一個字典。另一件要注意的事情;我認爲通過引用傳遞值的實現將有助於序列化。一旦數據存儲在內存中,我就想通了;它會包含一個明確的方法來處理。這是我的錯嗎?

這就是我的DataContractServiceContract

在這一點的實現是這樣的:

#region Using Reference... 

using System.Collections.Generic; 
using System.Threading.Tasks; 
using _2Do.Contract.Customer; 
using System.ServiceModel; 
using _2Do.Model.Customer; 
using System.Text; 
using System.Linq; 
using System; 

#endregion 

namespace _2Do.Service.Customer 
{ 

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] 
    public class PersonService : IPerson 
    { 

     #region Constructor: 

     PersonService() 
     { 

      // Empty Constructor. 

     } 

     #endregion 

     #region Implement Interface: 

     Person SetCustomer(Dictionary<Guid, Person> info) 
     { 

      // Receive an error that indicates; best overload method. 
      // Contains invalid arguments. 

     } 

     #endregion 

    } 

} 

然後我創建了一個單獨的項目來承載應用程序;我創建了一個空文本文件並將其重命名爲PersonService.svc

然後我把:<%@ ServiceHost Service = "_2Do.Service.Customer.PersonService" %>

哪一個應該指向正確的命名空間;其中在PersonService我有一個web.config文件,其中包含在Internet信息系統中主持的最低配置。我認爲這將允許我規避定義我的地址,綁定和合同。因爲IIS現在會爲我做這一切。

然後,我創建了一個ClientProxy類;其中包含的鏡子幾乎爲DataContract。然後,我創建了實際的客戶端應用程序做:

PersonProxy p = new PersonProxy(); 
p.First = txtFirst.Text; 
p.Last = txtLast.Text; 
p.Email = txtEmail.Text; 
p.Phone = txtPhone.Text; 
p.Site = txtSite.Text; 
Dictionary<Guid, Person> i = new Dictionary<Guid, Person>(); 
i.Add(Guid.NewGuid(), p); 

我已經得到了這些項目:

  • 型號 - > DataContract
  • 合同 - >的ServiceContract
  • 服務 - - >接口實現
  • 主機 - >存儲服務/服務
  • ClientProxy - >實現接口
  • 客戶端 - >鏈接到ClientProxy繼承的實際值。

這是我如何在客戶端上使用它。我不確定我在哪弄亂了這一點。我真的很想了解WCF,因爲我需要爲一個工作項目學習它。但我很沮喪,感覺很蠢,因爲我似乎無法解決這個問題。

如果我遵循教程,它的工作原理。但一旦我回到我原來的實施,它失敗了。不知道我要去哪裏或哪裏出錯。有些手會感激不盡;但如果你可以一步一步解釋它,那麼會很感激。所以我可以從我的錯誤中學習,以實際改善。

我無法將它存儲在服務器上的變量,傳輸,我不知道爲什麼在這一點上。

+0

我目前的猜測是您沒有正確設置數據合同類。我想你需要另一個從'Dictionary'派生並具有'CollectionDataContract'屬性集的類。我正在努力複製你的代碼來檢查。 – vlad

+0

如果你能把它工作,我將成爲有史以來最幸福的人;我真的無法得到這個愚蠢的服務。我感謝您的迴應和幫助。 – Greg

+0

奇怪,我的代碼似乎工作傳遞參數以及結果。你使用的是什麼版本的框架? – vlad

回答

2

我仍然不確定爲什麼你的代碼不工作。也許我不明白託管IIS以及我應該。但是,下面是我能想到的最簡單的Web服務+客戶端的完整工作示例。希望它能幫助你找到並解決問題。

首先,project structure:一個類庫項目中的契約,一個控制檯應用程序中的服務和主機,另一個控制檯應用程序中的客戶端。

數據合同:

[DataContract] 
public class Person 
{ 
    [DataMember] 
    public string Name { get; set; } 
    [DataMember] 
    public string Email { get; set; } 
} 

服務合同:

[ServiceContract] 
public interface IPerson 
{ 
    [OperationContract] 
    Person SetCustomer(Dictionary<Guid, Person> info); 
} 

服務:

public class PersonService : IPerson 
{ 
    public Person SetCustomer(Dictionary<Guid, Person> info) 
    { 
     foreach (var person in info.Values) 
     { 
      Console.WriteLine("Name: {0} | Email: {1}", person.Name, person.Email); 
     } 

     var p = new Person { Name = "John Doe", Email = "[email protected]" }; 
     return p; 
    } 
} 

服務主機(在主機項目的Program.cs中):

static void Main(string[] args) 
{ 
    using (ServiceHost host = new ServiceHost(typeof(PersonService), new Uri("http://localhost:8080"))) 
    { 
     host.Open(); 

     Console.WriteLine("Ready!"); 
     Console.ReadKey(true); 

     host.Close(); 
    } 
} 

最後,客戶端(在自己的項目的Program.cs):

static void Main(string[] args) 
{ 
    var binding = new BasicHttpBinding(); 
    var endpoint = new EndpointAddress("http://localhost:8080/"); 
    using (var factory = new ChannelFactory<IPerson>(binding, endpoint)) 
    { 
     var request = new Dictionary<Guid, Person>(); 
     request[Guid.NewGuid()] = new Person { Name = "Bob", Email = "[email protected]" }; 

     var client = factory.CreateChannel(); 
     var result = client.SetCustomer(request); 

     Console.WriteLine("Name: {0} | Email: {1}", result.Name, result.Email); 
     factory.Close(); 
    } 
    Console.ReadKey(true); 
} 

希望這有助於!

+0

在你的服務例子中;你添加一個新的項目。我假設來自服務器。如果我想把它顛倒過來怎麼辦?客戶端實際上爲服務器提供變量的位置。業務層可以寫入數據庫或寫入日誌。或者只是使用這些變量。 – Greg

+0

這是一篇不錯的文章,我將通過比較和審查,看看我能否弄清楚並讓你知道。 – Greg

+1

@Greg我的客戶創建了一個「字典」,並在將其發送到服務之前添加了一個元素。我這樣做是爲了測試發送是否可以正常工作。此外,該服務還會發送一個不同的「Person」對象,這在我的示例中也適用。 – vlad

1

您真的需要將字典作爲您的服務合同的參數嗎?保持簡單 - 改用List。我的建議是:

1)GUID屬性添加到合同的人數據

[DataContract(IsReference = true)] 
public class Person 
{ 
    #region Constructor: 

    public Person() 
    { 
     // Empty Constructor. 
    } 

    #endregion 

    #region Data Member Properties: 

    [DataMember] 
    public Guid Guid { get; set; } 

    [DataMember] 
    public string First { get; set; } 

    [DataMember] 
    public string Last { get; set; } 

    [DataMember] 
    public string Email { get; set; } 

    [DataMember] 
    public string Phone { get; set; } 

    [DataMember] 
    public string Site { get; set; } 

    #endregion 
} 

2)變更服務合同,使用一個列表:

[ServiceContract (Namespace = "https://_2Do") ] 
public interface IPerson 
{ 
    [OperationContract()] 
    Person SetCustomer(List<Person> info); 
} 

3)客戶端代碼會是這樣的:

PersonProxy p = new PersonProxy(); 
p.Guid = Guid.NewGuid(); 
p.First = txtFirst.Text; 
p.Last = txtLast.Text; 
p.Email = txtEmail.Text; 
p.Phone = txtPhone.Text; 
p.Site = txtSite.Text; 

var list = new List<Person>(); 
list.Add(p); 

最後,如果你真的需要你的服務代碼中的字典,你可以eas ily利用方法轉換列表ToDictionary

+0

感謝帖子;我會試試看。看看我的問題是在通用中,還是我搞砸了客戶端/組件分離。 – Greg