WCF: Сериализация и десериализация родовых коллекций
У меня есть команда класса, которая содержит общий список:
[DataContract(Name = "TeamDTO", IsReference = true)]
public class Team
{
[DataMember]
private IList<Person> members = new List<Person>();
public Team()
{
Init();
}
private void Init()
{
members = new List<Person>();
}
[System.Runtime.Serialization.OnDeserializing]
protected void OnDeserializing(StreamingContext ctx)
{
Log("OnDeserializing of Team called");
Init();
if (members != null) Log(members.ToString());
}
[System.Runtime.Serialization.OnSerializing]
private void OnSerializing(StreamingContext ctx)
{
Log("OnSerializing of Team called");
if (members != null) Log(members.ToString());
}
[System.Runtime.Serialization.OnDeserialized]
protected void OnDeserialized(StreamingContext ctx)
{
Log("OnDeserialized of Team called");
if (members != null) Log(members.ToString());
}
[System.Runtime.Serialization.OnSerialized]
private void OnSerialized(StreamingContext ctx)
{
Log("OnSerialized of Team called");
Log(members.ToString());
}
Когда я использую этот класс в службе WCF, я получаю следующий вывод журнала
OnSerializing of Team called
System.Collections.Generic.List 1[XXX.Person]
OnSerialized of Team called
System.Collections.Generic.List 1[XXX.Person]
OnDeserializing of Team called
System.Collections.Generic.List 1[XXX.Person]
OnDeserialized of Team called
XXX.Person[]
После десериализации members
является массивом и больше не является общим списком, хотя тип поля - IList<> (?!). Когда я пытаюсь отправить этот объект обратно через службу WCF, я получаю вывод журнала
OnSerializing of Team called
XXX.Person[]
После этого мой модульный тест завершается с ошибкой System.ExecutionEngineException, что означает, что служба WCF не может сериализовать массив. (возможно потому что ожидал IList<>)
Итак, мой вопрос: кто-нибудь знает, почему тип моего IList<> является массивом после десериализации и почему после этого я не могу сериализовать свой объект Team?
Спасибо
4 ответа
Вы столкнулись с одним из DataContractSerializer
подводные камни.
Исправлено: Измените вашу личную декларацию участника на:
[DataMember]
private List<Person> members = new List<Person>();
ИЛИ измените свойство на:
[DataMember()]
public IList<Person> Feedback {
get { return m_Feedback; }
set {
if ((value != null)) {
m_Feedback = new List<Person>(value);
} else {
m_Feedback = new List<Person>();
}
}
}
И это будет работать. Ошибка Microsoft Connect здесь
Эта проблема возникает при десериализации объекта с IList<T>
DataMember, а затем попробуйте снова сериализовать тот же экземпляр.
Если вы хотите увидеть что-то классное:
using System;
using System.Collections.Generic;
class TestArrayAncestry
{
static void Main(string[] args)
{
int[] values = new[] { 1, 2, 3 };
Console.WriteLine("int[] is IList<int>: {0}", values is IList<int>);
}
}
Будет печатать int[] is IList<int>: True
,
Я подозреваю, что это, возможно, причина, по которой вы видите, что он возвращается как массив после десериализации, но это не интуитивно понятно.
Если вы вызываете метод Add() на IList<int>
из массива, хотя, он бросает NotSupportedException
,
Одна из тех причуд.NET.
Я получил эту ошибку при транспортировке чтения IList из базы данных через LINQ. WCF размещался в IIS 7 на Windows Server 2008 x64.
Сбой пула приложений без предупреждений.
[ServiceBehavior]
public class Webservice : IWebservice
{
public IList<object> GetObjects()
{
return Database.Instance.GetObjects();
}
}
Это не совсем та же проблема, но может иметь ту же причину.
Решение было установить MS исправление KB973110 http://support.microsoft.com/kb/971030/en-us
Похоже, что ваша справочная служба WCF создает прокси-класс, а не использует существующий тип. Прокси-классы могут использовать только простые массивы, а не какие-либо специфические типы.NET, такие как универсальный список.
Чтобы избежать этого преобразования прокси-класса, на экране "Добавить ссылку на службу" нажмите кнопку "Дополнительно", а затем убедитесь, что установлен флажок "Повторное использование типов в ссылочных сборках". Это обеспечит использование существующего класса (с универсальным списком) при сериализации и десериализации объекта.
Взято прямо из моего блога. Я надеюсь, что это будет полезно:
Недавно я столкнулся с проблемой, когда мы использовали сервис WCF и использовали привязку пользовательской модели в нашем приложении ASP.net MVC. Все работало нормально, когда мы сериализовали наши ILists. По умолчанию IList сериализуется в массивы. В итоге я преобразовал наши массивы обратно в ILists с помощью Reflection и вызвал следующий метод в привязке пользовательской модели. Вот как выглядит метод:
public object ConvertArraysToIList(object returnObj)
{
if (returnObj != null)
{
var allProps = returnObj.GetType().GetProperties().Where(p => p.PropertyType.IsPublic
&& p.PropertyType.IsGenericType
&& p.PropertyType.Name==typeof(IList<>).Name).ToList();
foreach (var prop in allProps)
{
var value = prop.GetValue(returnObj,null);
//set the current property to a new instance of the IList<>
var arr=(System.Array)value;
Type listType=null;
if(arr!=null)
{
listType= arr.GetType().GetElementType();
}
//create an empty list of the specific type
var listInstance = typeof(List<>)
.MakeGenericType(listType)
.GetConstructor(Type.EmptyTypes)
.Invoke(null);
foreach (var currentValue in arr)
{
listInstance.GetType().GetMethod("Add").Invoke(listInstance, new[] { currentValue });
}
prop.SetValue(returnObj, listInstance, null);
}
}
return returnObj;
}