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[]
после этого мой модульный тест аварийно завершает работу с системой.Исключение executionengineexception, что означает, что в WCF служба не может сериализовать массив. (может быть, потому, что он ожидал IList)
Итак, мой вопрос: кто-нибудь знает, почему тип моего IList является массивом после десериализации и почему я больше не могу сериализовать свой объект Team?
спасибо
4 ответов
вы столкнулись с одним из DataContractSerializer
gotchas.
исправления: измените объявление частного участника на:
[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 и использовали пользовательскую модель binder в нашем ASP.net приложение MVC. Все работало отлично, когда мы сериализировали наши Илисты. IList по умолчанию сериализуется в массивы. Я закончил тем, что преобразовал наши массивы обратно в ILists, используя отражение и вызывая следующий метод в пользовательской модели binder. Вот как выглядит метод:
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;
}