2010-04-01 11 views
20

mam Zespół Klasa, która przechowuje listę Generic:WCF: Serializacja i deserializacji kolekcje generyczne

[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()); 
    } 

Kiedy użyć tej klasy w usług WCF, mam następujący wyjściowych dziennika

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[] 

Po deserializacji jest tablicą i nie jest już ogólną listą, chociaż typem pola jest IList <> (?!) Kiedy próbuję wysłać ten obiekt z powrotem przez usługę WCF, otrzymuję komunikat wyjściowy

OnSerializing of Team called 
XXX.Person[] 

Po tym moja jednostka wywala test z System.ExecutionEngineException, co oznacza usługę WCF nie jest w stanie serializować tablicę. (Może dlatego, że oczekuje się IList <>)

Więc moje pytanie brzmi: Czy ktoś wie dlaczego rodzaj mojego IList <> jest tablicą po deserializacji i dlatego nie mogę serializacji mojego obiektu zespołu już po tym ?

Dzięki

Odpowiedz

22

Napotkali Państwo na jedną z serii DataContractSerializer.

Fix: Zmiana oświadczenia prywatnego użytkownika do:

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

lub zmienić właściwość:

[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>(); 
     } 
    } 
} 

i będzie działać. Błąd Microsoft Connect jest następujący: here

Ten problem występuje podczas deserializacji obiektu z danymi DataMember IList<T>, a następnie spróbuj ponownie przekształcić to samo wystąpienie.

Jeśli chcesz zobaczyć coś fajnego:

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>); 
    } 
} 

będzie drukować int[] is IList<int>: True.

Podejrzewałem, że jest to prawdopodobnie powód, dla którego powrócisz jako tablica po deserializacji, ale jest to dość nieintuicyjne.

Jeśli jednak wywołasz metodę Add() na IList<int> tablicy, to wyśle ​​ona NotSupportedException.

Jedna z tych dziwactw .NET.

+3

Dzięki. Naprawdę ciężko jest dowiedzieć się, na co może wpłynąć "ja". W międzyczasie znalazłem inne rozwiązanie, szczególnie gdy potrzebujesz IList <> zamiast listy <> (np. Mój OR-Mapper może obsłużyć IList <>, ale nie List <>): zamiast oznaczać pole "członkowie" [ DataMember] oznaczyć właściwość i użyć tego kodu dla ustawiającego: if (value! = Null) members = new List (value); else members = new List (); Dzięki temu deserialized tablica jest przekazywana do settera, który tworzy z niego nową Listę. – Fabiano

+0

Ta ostatnia technika jest tym, co zawsze robię. To pole członka nie jest czymś, do czego powinien się kontaktować serializator. – IDisposable

+0

Po drugie, podejście własności! Jestem zły, że sam o tym nie myślałem. – codechurn

1

Brzmi jak twój odniesienie usług WCF jest stworzenie klasy proxy zamiast przy użyciu istniejącego typu. Klasy proxy mogą korzystać tylko z prostych tablic, a nie z konkretnych typów .NET, takich jak ogólna lista.

Aby uniknąć konwersji klasy pośredniej, na ekranie Dodawanie usługi serwisowej kliknij przycisk Zaawansowane, a następnie upewnij się, że zaznaczono "Ponownie używaj typów w zestawach z odsyłaczem". Zapewni to, że istniejąca klasa (z ogólną listą) jest używana podczas serializacji i deserializacji obiektu.

+0

Nie używam tutaj odnośnika do usługi. Mam odwołanie do mojego projektu WCF i programowo utworzyłem hosta i kanał. Tak więc ponownie używam tych typów samodzielnie. – Fabiano

+0

Rozumiem. W takim razie nie mam pojęcia. ;-) – jeremcc

2

Dostałem ten błąd podczas transportu ILista odczytanego z bazy danych przez LINQ. WCF był hostowany w IIS 7 na Windows Server 2008 x64.

Pula aplikacji zawiesiła się bez ostrzeżeń.

[ServiceBehavior] 
public class Webservice : IWebservice 
{ 

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

To nie jest dokładnie ten sam problem, ale może mieć tę samą przyczynę.

Rozdzielczość do było zainstalować MS Hotfix KB973110 http://support.microsoft.com/kb/971030/en-us

1
wyjęta prosto z mojego bloga

. Mam nadzieję, że będzie to pomocne:

Niedawno napotkano problem, w którym korzystaliśmy z usługi WCF i używaliśmy niestandardowego segregatora w naszej aplikacji ASP.net MVC. Wszystko działało dobrze, kiedy serializowaliśmy naszych ilistów. IList jest domyślnie serializowany do tablic. Skończyłem przekształcanie naszych tablic z powrotem w ILists za pomocą Reflection i wywołanie poniższej metody w niestandardowym segregatorze modelu. Oto jak wygląda metoda:

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; 
}