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

sea*_*ean 4 c# wcf serialization datacontractserializer

我试图通过 WCF 传递一个带有 Dictionary 属性的类,对于一种方法失败,但对于另一种方法有效。当类在 a 内返回时List,它就起作用了。但是当类在 a 中返回时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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public List<DetailLog> GetDetailLog(List<int> IDs, List<int> actionTypeIds, List<int> userIds, DateTime beginDate, DateTime endDate)
Run Code Online (Sandbox Code Playgroud)

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

但我需要传递 DetailLog 类,因此我创建了该类型的 DataTable 列:

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

此方法在 WCF 主机端可以正常退出,但在客户端会出现连接丢失的错误。我尝试在界面中为OperationContract添加ServiceKnownType,但没有修复它:

[OperationContract]
[ServiceKnownType(typeof(DetailLog))]
DataTable GetCustomUserAuditReport(int CustomReportID, List<CustomReportFilter> reportFilters);
Run Code Online (Sandbox Code Playgroud)

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

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

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

Cannot serialize member ... of type System.Collections.Generic.Dictionary`2 because it implements IDictionary.
Run Code Online (Sandbox Code Playgroud)

dbc*_*dbc 6

您的问题如下:

  1. Dictionary<TKey, TValue>由WCF 中使用的数据协定序列化器支持,如文档中所述。这就是为什么DetailLogWCF 可以将您的类作为根对象成功地通过网络发送。

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

  3. DataTable实现IXmlSerializable.

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

  5. XmlSerializer不支持字典。因此,当您DetailLog嵌套DataTable.

  6. 作为一个无关的问题,你还需要设置数据表名,如果不设置,序列化会抛出异常:

        data.TableName = "CustomDetailReport"; // For instance
    
    Run Code Online (Sandbox Code Playgroud)

要解决字典的问题,您需要使DetailLog两个序列化器都可以序列化您的类。问题如何在Dictionary<int, string>不使用 XElement 的情况下从自定义 XML 序列化/反序列化?给出了多种方法来序列化其字典属性,包括使用代理数组属性:

[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; }
}
Run Code Online (Sandbox Code Playgroud)

WCF 现在应该能够DataTable通过网络成功发送您的内容。