2015-12-16 1 views
2

J'essaie de passer une classe avec une propriété Dictionary sur WCF et elle échoue pour une méthode mais fonctionne pour une autre. Lorsque la classe est retournée dans un List, cela fonctionne. Mais lorsque la classe est renvoyée à l'intérieur d'un DataTable, le client indique simplement que la connexion a été déconnectée et qu'aucune erreur n'apparaît.Impossible de sérialiser le membre ... de type System.Collections.Generic.Dictionary`2 car il implémente IDictionary

Voici les questions faisant de classe:

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

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

    [DataMember] 
    public Dictionary<string, string> Fields 
    { 
     get; 
     set; 
    } 

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

moi avons commencé la création d'une méthode qui fonctionne sans problème:

public List<DetailLog> GetDetailLog(List<int> IDs, List<int> actionTypeIds, List<int> userIds, DateTime beginDate, DateTime endDate) 

Ensuite, nous avons besoin de créer des rapports très dynamiques si je DataTable que nous avons utilisé précédemment pour d'autres rapports dynamiques.

Mais je devais passer le long de la classe DetailLog donc je créé une colonne DataTable de ce type:

public DataTable GetCustomDetailReport(int CustomReportID, List<CustomReportFilter> reportFilters) 
{ 
DataTable data = new DataTable(); 
... 
data.Columns.Add("DetailLog", typeof(DetailLog)); 
... 
} 

Cette méthode sortirait bien sur le côté hôte WCF mais le côté client aurait erreur sur la connexion être perdu. J'ai essayé d'ajouter ServiceKnownType pour la OperationContract dans l'interface, mais il n'a pas le réparer:

[OperationContract] 
[ServiceKnownType(typeof(DetailLog))] 
DataTable GetCustomUserAuditReport(int CustomReportID, List<CustomReportFilter> reportFilters); 

Je ne peux pas vraiment déboguer la sérialisation lorsque la méthode retourne le DataTable alors j'ai ajouté ce code à la fin du GetCustomDetailReport() à attraper l'erreur.

DataContractSerializer ser = new DataContractSerializer(typeof(DataTable), new List<Type> { typeof(DetailLog) }); 
ser.WriteObject(Stream.Null, data); 

Quand je l'ai fait, j'ai vu une exception

Cannot serialize member ... of type System.Collections.Generic.Dictionary`2 because it implements IDictionary. 

Répondre

2

Votre question est la suivante:

  1. Dictionary<TKey, TValue> est pris en charge par le data contract serializer utilisé dans WCF, comme expliqué dans le docs. C'est pourquoi votre classe DetailLog peut être envoyée sur le réseau avec succès par WCF en tant qu'objet racine.

  2. Ce sérialiseur prend également en charge IXmlSerializable pour permettre aux types de se sérialiser manuellement en XML.

  3. DataTable implémente IXmlSerializable.

  4. interne, DataTable sérialise les entrées non-primitifs utilisant XmlSerializer - un sérialiseur complètement différent qui utilise une base de code complètement différent.

  5. XmlSerializer does not support dictionaries. Ainsi, votre DetailLog ne peut pas être envoyé sur le réseau par WCF lorsqu'il est imbriqué dans un DataTable.

  6. En tant que problème non lié, vous devez également définir le nom de la table de données, la sérialisation lancera une exception si vous ne le faites pas:

    data.TableName = "CustomDetailReport"; // For instance 
    

Pour contourner le problème avec les dictionnaires, vous avez besoin pour rendre votre classe DetailLog sérialisable par les deux sérialiseurs. La question How to serialize/deserialize to Dictionary<int, string> from custom XML not using XElement? donne une variété de façons de sérialiser sa propriété dictionnaire, y compris en utilisant une propriété de tableau proxy:

[XmlType("KeyValue"), XmlRoot("KeyValue")] 
public class SerializableKeyValuePair<TKey, TValue> 
{ 
    public TKey Key { get; set; } 
    public TValue Value { get; set; } 
} 

public static class SerializableKeyValuePairExtensions 
{ 
    public static SerializableKeyValuePair<TKey, TValue> ToSerializablePair<TKey, TValue>(this KeyValuePair<TKey, TValue> pair) 
    { 
     return new SerializableKeyValuePair<TKey, TValue> { Key = pair.Key, Value = pair.Value }; 
    } 
} 

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

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

    [DataMember] 
    [XmlIgnore] 
    public Dictionary<string, string> Fields { get; set; } 

    [IgnoreDataMember] 
    [XmlArray("Fields")] 
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)] 
    public SerializableKeyValuePair<string, string>[] FieldsArray 
    { 
     get 
     { 
      return Fields == null ? null : Fields.Select(p => p.ToSerializablePair()).ToArray(); 
     } 
     set 
     { 
      Fields = value == null ? null : value.ToDictionary(p => p.Key, p => p.Value); 
     } 
    } 

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

WCF devrait maintenant être en mesure d'envoyer votre DataTable avec succès sur le fil.

+0

Merci pour cette explication complète! – sean