无法序列化类型System.Collections.Generic.Dictionary`2的成员...因为它实现了IDictionary

时间:2015-12-16 16:01:08

标签: c# wcf serialization datacontractserializer

我试图通过WCF传递一个带有Dictionary属性的类,并且它对于一个方法失败但对另一个方法有效。当类在List内返回时,它可以正常工作。但是当在DataTable内返回类时,客户端只是说连接断开连接并且没有出现错误。

以下是导致问题的类:

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

我开始创建一个无问题的方法:

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

然后我们需要创建一些非常动态的报告,因此我使用了之前用于其他动态报告的DataTable。

但是我需要传递DetailLog类,所以我创建了一个这种类型的DataTable列:

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

此方法将在WCF主机端正常退出,但客户端会因连接丢失而出错。我尝试在接口中为OperationContract添加ServiceKnownType但它没有修复它:

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

当方法返回DataTable时,我无法真正调试序列化,因此我将此代码添加到GetCustomDetailReport()的末尾以捕获错误。

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

当我这样做时,我看到了异常

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

1 个答案:

答案 0 :(得分:2)

您的问题如下:

  1. Dictionary<TKey, TValue>受WCF中使用的data contract serializer支持,如docs中所述。这就是WCF作为根对象成功通过线路发送DetailLog类的原因。

  2. 此序列化程序还支持IXmlSerializable以允许类型手动将自身序列化为XML。

  3. DataTable实施IXmlSerializable

  4. 在内部,DataTable使用XmlSerializer 序列化非原始条目 - 一个使用完全不同的代码库的完全不同的序列化程序。

    < / LI>
  5. XmlSerializer does not support dictionaries。因此,嵌套在DetailLog 时,WCF 无法通过网络发送DataTable

  6. 作为一个无关的问题,您还需要设置数据表名称,如果不这样做,序列化将引发异常:

        data.TableName = "CustomDetailReport"; // For instance
    
  7. 要解决字典问题,您需要使两个序列化程序可以序列化DetailLog类。问题How to serialize/deserialize to Dictionary<int, string> from custom XML not using XElement?提供了各种序列化其字典属性的方法,包括使用代理数组属性:

    [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现在应该能够成功发送您的DataTable