无法序列化 System.Collections.Generic.Dictionary`2 类型的成员 ... 因为它实现了 IDictionary [英] Cannot serialize member ... of type System.Collections.Generic.Dictionary`2 because it implements IDictionary

查看:350
本文介绍了无法序列化 System.Collections.Generic.Dictionary`2 类型的成员 ... 因为它实现了 IDictionary的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试通过 WCF 传递一个具有 Dictionary 属性的类,它对一种方法失败,但对另一种方法有效.当该类在 List 内返回时,它会起作用.但是当在 DataTable 中返回该类时,客户端只是说连接已断开并且没有出现错误.

这是导致问题的类:

[数据契约]公共类 DetailLog{[数据成员]公共字符串名称{得到;放;}[数据成员]公共字符串 SubAction{得到;放;}[数据成员]公共字典<字符串,字符串>字段{得到;放;}[数据成员]公共字符串用户名{得到;放;}}

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

public ListGetDetailLog(List IDs, List actionTypeIds, List userIds, DateTime beginDate, DateTime endDate)

然后我们需要创建一些非常动态的报告,所以我使用了我们以前用于其他动态报告的 DataTable.

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

public DataTable GetCustomDetailReport(int CustomReportID, List reportFilters){数据表数据 = 新数据表();...data.Columns.Add("DetailLog", typeof(DetailLog));...}

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

[操作契约][ServiceKnownType(typeof(DetailLog))]DataTable GetCustomUserAuditReport(int CustomReportID, List reportFilters);

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

DataContractSerializer ser = new DataContractSerializer(typeof(DataTable), new List { typeof(DetailLog) });ser.WriteObject(Stream.Null, 数据);

当我这样做时,我看到了一个例外

<块引用>

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

解决方案

您的问题如下:

  1. Dictionary数据协定序列化程序 在 WCF 中使用,如 文档.这就是为什么您的 DetailLog 类可以通过 WCF 作为根对象成功通过网络发送.

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

  3. DataTable 实现了 IXmlSerializable.

  4. 在内部,DataTable 使用XmlSerializer -- 一个完全不同的序列化器,使用完全不同的代码库.

  5. XmlSerializer 不支持字典.因此,当嵌套在 DataTable 中时,您的 DetailLog 无法通过 WCF 通过网络发送.

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

     data.TableName = "CustomDetailReport";//例如

要解决字典的问题,您需要使 DetailLog 类可被两个序列化器序列化.问题 如何序列化/反序列化为Dictionary 来自不使用 XElement 的自定义 XML? 提供了多种方法来序列化其字典属性,包括使用代理数组属性:

[XmlType("KeyValue"), XmlRoot("KeyValue")]公共类 SerializableKeyValuePair{公共 TKey 密钥 { 获取;放;}公共 TValue 值 { 获取;放;}}公共静态类 SerializableKeyValuePairExtensions{公共静态 SerializableKeyValuePairToSerializablePair(这个 KeyValuePair 对){返回新的 SerializableKeyValuePair{ Key = pair.Key, Value = pair.Value };}}[数据合约]公共类 DetailLog{[数据成员]公共字符串名称{获取;放;}[数据成员]公共字符串 SubAction { 获取;放;}[数据成员][XmlIgnore]公共字典<字符串,字符串>字段{获取;放;}[忽略数据成员][XmlArray("字段")][Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]public SerializableKeyValuePair[] FieldsArray{得到{返回字段 == null ?null : Fields.Select(p => p.ToSerializablePair()).ToArray();}放{字段 = 值 == 空?null : value.ToDictionary(p => p.Key, p => p.Value);}}[数据成员]公共字符串用户名 { 获取;放;}}

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

I am trying to pass a class with a Dictionary property over WCF and it is failing for one method but works for another. When the class is returned inside a List, it works. But when the class is returned inside a DataTable, the client just says the connection was disconnected and no error shows up.

Here is the class causing issues:

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

I started out creating a method that works without issue:

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

Then we needed to create some very dynamic reports so I used a DataTable which we have used previously for other dynamic reports.

But I needed to pass along the DetailLog class so I created a DataTable column of that type:

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

This method would would exit fine on the WCF host side but the client side would error about the connection being lost. I tried adding ServiceKnownType for the OperationContract in the interface but it did not fix it:

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

I cannot really debug the serialization when the method returns the DataTable so I added this code to the end of the GetCustomDetailReport() to catch the error.

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

When I did, I saw an exception

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

解决方案

Your issue is as follows:

  1. Dictionary<TKey, TValue> is supported by the data contract serializer used in WCF, as explained in the docs. This is why your DetailLog class can be sent over the wire successfully by WCF as a root object.

  2. This serializer also supports IXmlSerializable to allow types to manually serialize themselves to XML.

  3. DataTable implements IXmlSerializable.

  4. Internally, DataTable serializes non-primitive entries using XmlSerializer -- a completely different serializer that uses a completely different code base.

  5. XmlSerializer does not support dictionaries. Thus your DetailLog cannot be sent over the wire by WCF when nested in a DataTable.

  6. As an unrelated problem, you also need to set the data table name, serialization will throw an exception if you do not:

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

To work around the issue with dictionaries, you need to make your DetailLog class serializable by both serializers. The question How to serialize/deserialize to Dictionary<int, string> from custom XML not using XElement? gives a variety of ways to serialize its dictionary property, including using a proxy array property:

[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 should now be able to send your DataTable over the wire successfully.

这篇关于无法序列化 System.Collections.Generic.Dictionary`2 类型的成员 ... 因为它实现了 IDictionary的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆