无法序列化 System.Collections.Generic.Dictionary`2 类型的成员 ... 因为它实现了 IDictionary [英] Cannot serialize member ... of type System.Collections.Generic.Dictionary`2 because it implements 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.
您的问题如下:
Dictionary
由 数据协定序列化程序 在 WCF 中使用,如 文档.这就是为什么您的DetailLog
类可以通过 WCF 作为根对象成功通过网络发送.此序列化程序还支持
IXmlSerializable
允许类型将自己手动序列化为 XML.DataTable
实现了IXmlSerializable
.在内部,
DataTable
使用XmlSerializer
-- 一个完全不同的序列化器,使用完全不同的代码库.XmlSerializer
不支持字典.因此,当嵌套在DataTable
中时,您的DetailLog
无法通过 WCF 通过网络发送.作为一个无关的问题,你还需要设置数据表名,如果不设置,序列化会抛出异常:
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:
Dictionary<TKey, TValue>
is supported by the data contract serializer used in WCF, as explained in the docs. This is why yourDetailLog
class can be sent over the wire successfully by WCF as a root object.This serializer also supports
IXmlSerializable
to allow types to manually serialize themselves to XML.DataTable
implementsIXmlSerializable
.Internally,
DataTable
serializes non-primitive entries usingXmlSerializer
-- a completely different serializer that uses a completely different code base.XmlSerializer
does not support dictionaries. Thus yourDetailLog
cannot be sent over the wire by WCF when nested in aDataTable
.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屋!