Сериализация данных WCF: может ли она идти быстрее?
Этот вопрос является своего рода продолжением этого вопроса.
Когда мы хотим создать сервис WCF, который работает с какими-то данными, естественно, мы хотим, чтобы он был быстрым и эффективным. Чтобы достичь этого, мы должны обеспечить максимально быструю работу всех сегментов дорожного движения данных, от серверного хранилища данных, такого как SQL Server, до клиента WCF, который запрашивал эти данные.
Стремясь найти ответ на этот предыдущий вопрос, мы узнали, благодаря Slauma и другим, кто внес свой вклад в комментарии, что трудоемкая часть (первого) большого запроса Entity Framework - это материализация объекта и присоединение сущностей к контексту, когда результат от база данных возвращается. Мы видели, что на последующих запросах все работает намного быстрее.
Предполагая, что эти большие запросы используются как операции только для чтения, мы пришли к выводу, что мы можем установить EF MergeOption
в NoTracking
, давая лучшую производительность первого запроса. Что мы сделали с NoTracking
говорил EF создать отдельный объект для каждой записи, извлеченной из базы данных - даже если у них одинаковый ключ. Это вызовет дополнительную обработку, если у нас есть .Include()
оператор в нашем запросе, который приведет к возвращению данных с гораздо большим размером.
Данные могут быть настолько большими, что мы могли бы легко спросить себя - действительно ли мы помогли нашему делу, используя NoTracking
вариант, даже если мы сделали запрос быстрее (и, возможно, только первый, в зависимости от количества .Include()
заявления, потому что последующие запросы без NoTracking
вариант с несколькими .Include()
заявления бегут быстрее, потому что NoTracking
опция заставляет создавать намного больше объектов, когда данные возвращаются с сервера)?
Самая большая проблема заключается в том, как эффективно сериализовать этот объем данных и десериализовать его на клиенте. С сериализацией уже так медленно, как есть (я использую DataContractSerializer
с PreserveObjectReferences
установлен в true
потому что я отправляю сгенерированные POCO EF 4.x моему клиенту и наоборот), хотим ли мы генерировать еще больше данных (благодаря NoTracking
)? Если честно, я не видел данных, полученных в результате запроса с NoTracking
опция для ~11.000 объектов, не включая свойства навигации, полученные с помощью .Include()
, прибывающих на стороне клиента еще. В прошлый раз, когда я пытался это осуществить, сработало время ожидания 00:10:00 (!)
Так что, если вы все еще читаете эту стену текста, вы говорите мне, как решить эту ситуацию. Какой сериализатор использовать для достижения приемлемых результатов? В настоящее время, если я не использую NoTracking
опция, сериализация, транспорт и десериализация ~11.000, через wsHttpBinding
-подобная привязка на локальной машине занимает ~5 секунд. Что меня пугает, так это то, что эта большая таблица, скорее всего, в конечном итоге будет содержать ~500.000 записей.
3 ответа
Рассматривали ли вы создание модели представления для вашего объекта и выполнение проекции в операторе выбора. Это должно быть намного быстрее, поэтому:
var result = from person in DB.Entities.Persons
.Include("District")
.Include("District.City")
.Include("District.City.State")
.Include("Nationality")
select new PersonViewModel()
{
Name = person.Name,
City = person.District.City,
State = person.District.City.State
Nationality = person.Nationality.Name
};
Для этого потребуется создать класс View Model для хранения сглаженных данных для PersonViewModel.
Возможно, вам удастся еще больше ускорить процесс, создав представление базы данных и позволяя Entity Framework выбирать прямо оттуда.
Если вы хотите, чтобы клиентский интерфейс заполнил сетку 500000 записей, то я бы вообще удалил слой веб-сервиса и использовал DataReader для ускорения процесса. Entity Framework и WCF не подходят для преобразования данных с надлежащей производительностью. Что вы в основном делаете здесь:
База данных -> TDS -> .NET объекты -> XML -> Простой текст -> XML -> .NET Objects -> UI
Хотя это может быть легко уменьшено до:
База данных -> TDS -> Пользовательский интерфейс
Затем используйте EntityFramwork для обработки изменений сущностей в вашей бизнес-логике. Это соответствует шаблону разделения команд и запросов. Используйте технологию, подходящую для высокопроизводительного запроса данных, и свяжите ее непосредственно с вашим приложением. Затем используйте командную стратегию для реализации вашей бизнес-логики.
Службы OData также могут предоставить лучший способ привязки вашего пользовательского интерфейса непосредственно к данным, поскольку его можно использовать для быстрого запроса ваших данных, что позволяет вам осуществлять быструю фильтрацию, не обращая на это внимания пользователя.
Если параметры безопасности запрещают прямой запрос через OData или прямой доступ к базе данных SQL, рассмотрите возможность материализации объектов самостоятельно. Выберите данные непосредственно из представления или запроса и используйте IDataReader
чтобы напрямую заполнить вашу View Model. Это, вероятно, даст вам высочайшую производительность.
Существует множество альтернатив для Entity Framework, созданных особенно потому, что EF не предназначен для больших наборов данных. См. FluentData DapperDotNet, Massive или PetaPoco. Возможно, вы захотите использовать их параллельно с Entity Framework для обработки ваших больших, плоских запросов данных.
Я использую реализацию Bson от Json.Net в своем приложении RIA. Больше информации здесь.
Я возвращаю IEnumerable при чтении из базы данных и сериализации строк. Я считаю скорость приемлемой и возвращаю сущности с примерно 20 свойствами. Такой подход должен минимизировать одновременное использование памяти на сервере.
Исходя из того, что я собрал, просмотрев различные обзоры и тесты производительности, я бы выбрал protobuf-net в качестве сериализатора. Это просто вопрос дизайна, может ли он быть подключен к моей конфигурации сервиса. Больше информации об этом здесь.
Хотя это не был полностью ответ на этот вопрос, у jessehouwing был лучший ответ, и я jessehouwing его как принятый.