2010-04-01 111 views
20

我有持有泛型列表類團隊:WCF:序列化和反序列化泛型集合

[DataContract(Name = "TeamDTO", IsReference = true)] 
public class Team 
{ 
    [DataMember] 
    private IList<Person> members = new List<Person>(); 

    public Team() 
    { 
     Init(); 
    } 

    private void Init() 
    { 
     members = new List<Person>(); 
    } 

    [System.Runtime.Serialization.OnDeserializing] 
    protected void OnDeserializing(StreamingContext ctx) 
    { 
     Log("OnDeserializing of Team called"); 
     Init(); 
     if (members != null) Log(members.ToString()); 
    } 

    [System.Runtime.Serialization.OnSerializing] 
    private void OnSerializing(StreamingContext ctx) 
    { 
     Log("OnSerializing of Team called"); 
     if (members != null) Log(members.ToString()); 
    } 

    [System.Runtime.Serialization.OnDeserialized] 
    protected void OnDeserialized(StreamingContext ctx) 
    { 
     Log("OnDeserialized of Team called"); 
     if (members != null) Log(members.ToString()); 
    } 

    [System.Runtime.Serialization.OnSerialized] 
    private void OnSerialized(StreamingContext ctx) 
    { 
     Log("OnSerialized of Team called"); 
     Log(members.ToString()); 
    } 

當我在一個WCF服務中使用這個類,我獲得以下日誌輸出

OnSerializing of Team called  
System.Collections.Generic.List 1[XXX.Person] 

OnSerialized of Team called  
System.Collections.Generic.List 1[XXX.Person] 

OnDeserializing of Team called  
System.Collections.Generic.List 1[XXX.Person] 

OnDeserialized of Team called  
XXX.Person[] 

反序列化後members是一個數組,不再是通用列表,雖然字段類型是IList <>(?!) 當我嘗試通過WCF服務發送此對象時,我得到日誌輸出

OnSerializing of Team called 
XXX.Person[] 

之後我的單元測試崩潰了System.ExecutionEngineException,這意味着WCF服務無法序列化數組。 (也許是因爲它預計IList <>)

所以,我的問題是:有人知道爲什麼我的IList <>是反序列化後的數組,爲什麼我不能序列化我的Team對象之後?

感謝

回答

22

您已經遇到DataContractSerializer陷阱之一。

修復:您的私有成員聲明更改爲:

[DataMember] 
private List<Person> members = new List<Person>(); 

或屬性更改爲:

[DataMember()] 
public IList<Person> Feedback { 
    get { return m_Feedback; } 
    set { 
     if ((value != null)) { 
      m_Feedback = new List<Person>(value); 

     } else { 
      m_Feedback = new List<Person>(); 
     } 
    } 
} 

,它會工作。 Microsoft Connect錯誤是here

當您用IList<T> DataMember反序列化對象並嘗試再次序列化同一實例時,會出現此問題。

如果你想看到的東西很酷:

using System; 
using System.Collections.Generic; 

class TestArrayAncestry 
{ 
    static void Main(string[] args) 
    { 
     int[] values = new[] { 1, 2, 3 };   
     Console.WriteLine("int[] is IList<int>: {0}", values is IList<int>); 
    } 
} 

這將打印int[] is IList<int>: True

我懷疑這可能是你看到它在反序列化後作爲數組返回的原因,但它非常不直觀。

如果您在數組的IList<int>上調用Add()方法,它會拋出NotSupportedException

.NET怪癖之一。

+3

謝謝。真的很難找出「我」可能會產生什麼影響。與此同時,我發現了另一種解決方案,特別是當您需要IList <>而不是List <>時(例如,我的OR-Mapper只能處理IList <>而不是List <>):而不是用[ DataMember]標記屬性並使用此代碼作爲setter:if(value!= null)members = new List (value); else members = new List (); 用這個反序列化數組傳遞給setter,它從中創建一個新List。 – Fabiano 2010-04-06 08:59:32

+0

後一種技術是我總是這樣做的。該成員字段不是序列化程序應該觸及的東西。 – IDisposable 2012-04-13 19:55:51

+0

我第二個屬性的方法!我很傷心,我沒有想到自己。 – codechurn 2012-11-06 21:16:37

1

這聽起來像WCF服務引用創建一個代理類,而不是使用現有的類型。代理類只能使用簡單的數組,而不能使用像通用列表這樣的.NET特定類型。

要避免此代理類轉換,請在「添加服務參考」屏幕中單擊「高級」按鈕,然後確保選中「在引用的程序集中重用類型」。這將確保在序列化和反序列化對象時使用現有的類(使用通用列表)。

+0

我在這裏沒有使用服務引用。我有一個對我的WCF項目的參考,並以編程方式創建主機和頻道。所以,我自己重複使用了類型 – Fabiano 2010-04-01 23:36:01

+0

我明白了。那麼在那種情況下,我不知道。 ;-) – jeremcc 2010-04-02 16:01:12

2

我在通過LINQ從數據庫中傳輸IList讀取時遇到此錯誤。 WCF託管在Windows Server 2008 x64上的IIS 7中。

應用程序池崩潰,沒有任何警告。

[ServiceBehavior] 
public class Webservice : IWebservice 
{ 

    public IList<object> GetObjects() 
    { 
     return Database.Instance.GetObjects(); 
    } 
} 

它不完全相同的問題,但可能有同樣的原因。

的分辨率是安裝MS修補程序KB973110 http://support.microsoft.com/kb/971030/en-us

1

從我的博客直接服用。我希望它會有幫助:

我最近遇到了一個問題,那就是我們正在使用WCF服務並在我們的ASP.net MVC應用程序中使用自定義模型聯編程序。當我們序列化我們的IList時,一切正常。 IList默認序列化爲數組。我最終使用Reflection將我們的數組轉換回IList,並在自定義模型聯編程序中調用以下方法。下面是方法的樣子:

public object ConvertArraysToIList(object returnObj)  
{ 

if (returnObj != null) 
{ 
    var allProps = returnObj.GetType().GetProperties().Where(p => p.PropertyType.IsPublic 
     && p.PropertyType.IsGenericType 
     && p.PropertyType.Name==typeof(IList<>).Name).ToList(); 

    foreach (var prop in allProps) 
    { 
     var value = prop.GetValue(returnObj,null); 
     //set the current property to a new instance of the IList<> 
     var arr=(System.Array)value; 
     Type listType=null; 

     if(arr!=null) 
     { 
      listType= arr.GetType().GetElementType(); 
     } 

     //create an empty list of the specific type 
     var listInstance = typeof(List<>) 
      .MakeGenericType(listType) 
      .GetConstructor(Type.EmptyTypes) 
      .Invoke(null); 

     foreach (var currentValue in arr) 
     { 
      listInstance.GetType().GetMethod("Add").Invoke(listInstance, new[] { currentValue }); 
     } 

     prop.SetValue(returnObj, listInstance, null); 
    } 
} 

return returnObj; 
}