С# Compact Framework — OutOfMemoryException с XmlSerializer.Serialize

Я пытаюсь сериализовать большую коллекцию объектов (20 000) объектов в коллекции. Я делаю это, используя следующий код:

XmlSerializer xs = new XmlSerializer(deserialized.GetType());
StringWriter sw;
using (sw = new StringWriter())
{
   xs.Serialize(sw, deserialized);   // OutOfMemoryException here
}

string packet = sw.ToString();
return packet;

Есть ли лучший способ сделать это, или я делаю что-то явно неправильно?


person djdd87    schedule 28.04.2009    source источник
comment
Спасибо за обновление метрик - ценю. Удовлетворительны ли эти сроки? Для информации: CF 3.5 снова работает быстрее, так как он может использовать Delegate.CreateDelegate для оптимизации доступа к свойствам.   -  person Marc Gravell    schedule 06.05.2009
comment
Нет, спасибо, так намного быстрее. При использовании XmlSerializer для 20000 записей требуется 3 минуты 50 секунд.   -  person djdd87    schedule 06.05.2009


Ответы (3)


Похоже, что это должно работать, но у CF есть непредсказуемые ограничения.

Является ли xml требованием? Я не помню, чтобы пробовал это с 20 тыс. записей, но другим вариантом может быть попробовать использовать другой сериализатор, например, protobuf-net работает на CF2. Я не могу гарантировать, что это сработает, но, возможно, стоит попробовать.

(в частности, в настоящее время я рефакторинг кода, чтобы попытаться обойти некоторые дополнительные "универсальные" ограничения внутри CF - но если у вас нет очень сложной объектной модели, это не должно на вас влиять).


Пример использования; обратите внимание, что этот пример также работает нормально для XmlSerializer, но protobuf-net использует только 20% пространства (или 10% пространства, если учесть, что каждый символ занимает в памяти два байта):

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
using ProtoBuf;

[Serializable, ProtoContract]
public class Department
{
    [ProtoMember(1)]
    public string Name { get; set; }
    [ProtoMember(2)]
    public List<Person> People { get; set; }
}

[Serializable, ProtoContract]
public class Person
{
    [ProtoMember(1)]
    public int Id { get; set; }
    [ProtoMember(2)]
    public string Name { get; set; }
    [ProtoMember(3)]
    public DateTime DateOfBirth { get; set; }
}


static class Program
{
    [MTAThread]
    static void Main()
    {
        Department dept = new Department { Name = "foo"};
        dept.People = new List<Person>();
        Random rand = new Random(123456);
        for (int i = 0; i < 20000; i++)
        {
            Person person = new Person();
            person.Id = rand.Next(50000);
            person.DateOfBirth = DateTime.Today.AddDays(-rand.Next(2000));
            person.Name = "fixed name";
            dept.People.Add(person);
        }

        byte[] raw;
        using (MemoryStream ms = new MemoryStream())
        {
            Serializer.Serialize(ms, dept);
            raw = ms.ToArray(); // 473,399 bytes
        }

        XmlSerializer ser = new XmlSerializer(typeof(Department));
        StringWriter sw = new StringWriter();
        ser.Serialize(sw, dept);
        string s = sw.ToString(); // 2,115,693 characters
    }
}

Дайте мне знать, если вам нужна дополнительная помощь - я могу говорить об этом весь день ;-p Обратите внимание, что это может работать только со стандартными атрибутами xml ([XmlElement(Order=1)]) - я использовал более конкретные атрибуты [ProtoMember(1)] и т. д. для ясности. Это также позволяет точно контролировать сериализацию (зигзагообразный или двухкомпонентный, сгруппированный или с префиксом длины и т. д.).

person Marc Gravell    schedule 28.04.2009
comment
Просто добавлю, благодаря Protobuf.Net, теперь я сериализую, а затем сохраняю в файл 20 000 записей (в моей самой большой структуре таблицы) за 29 секунд. Затем я могу загрузить этот файл обратно с помощью десериализатора за 28 секунд). - person djdd87; 06.05.2009

Есть ли у вас какие-либо показатели потребления памяти вашим приложением? Я предполагаю, что вы работаете на WM, что означает, что адресное пространство каждого процесса ограничено 32 МБ. С большим XML возможно, что у вас действительно закончилась память.

person Tal Pressman    schedule 28.04.2009

Возможно, вы могли бы рассмотреть возможность сохранения отдельных объектов (вместо сохранения коллекции как одного большого блока). Если это так, вы можете использовать проект NFileStorage, который я создал на codeplex; nfilestorage.codeplex.com (этот файл не создан специально для CF, поэтому не могу сказать, совместим ли он с этим)...

Удачи, Герт-Ян

person Community    schedule 26.05.2009
comment
Если вы знаете, что ваш проект работает с CF, у меня нет проблем с тем, что вы предлагаете его в качестве решения, но если вы не знаете, не публикуйте его в надежде просто привлечь внимание. Протестируйте сценарий самостоятельно и, если он сработает, тогда сообщите нам об этом. - person ctacke; 26.05.2009