Медленный конструктор SoapHttpClientProtocol
Я делаю некоторые эксперименты с Microsoft Dynamics CRM. Вы взаимодействуете с ним через веб-сервисы, и я добавил веб-ссылку в свой проект. Интерфейс веб-службы очень богат, и сгенерированный "Reference.cs" имеет размер около 90 КБ.
Я использую веб-ссылку в консольном приложении. Я часто что-то меняю, перекомпилирую и запускаю. Компиляция выполняется быстро, но обновление ссылки на веб-сервис происходит очень медленно, это занимает около 15-20 секунд:
CrmService service = new CrmService();
Профилирование показывает, что все время тратится в конструкторе SoapHttpClientProtocol.
Виновным является, по-видимому, тот факт, что код сериализации XML (не включенный в упомянутый выше 90-килобайтный код) генерируется во время выполнения перед JIT-обработкой. Это происходит во время вызова конструктора. Ожидание довольно разочаровывает, когда вы играете и пробуете что-то новое.
Я пробовал различные комбинации sgen.exe, ngen и XGenPlus (что занимает несколько часов и генерирует 500 МБ дополнительного кода), но безрезультатно. Я подумал о реализации службы Windows, в которой есть несколько экземпляров CrmService, готовых к работе в случае необходимости, но это кажется чрезмерным.
Есть идеи?
6 ответов
Следующее вырвано из этой темы на форумах VMWare:
Привет народ,
Мы обнаружили, что sgen.exe работает. Просто есть несколько дополнительных шагов помимо предварительной генерации dll-файлов сериализатора, которые мы пропустили в этой теме. Вот подробная инструкция
ПРОБЛЕМА
При использовании VIM 2.0 SDK из.NET требуется длительное время для создания экземпляра класса VimService. (Класс VimService - это прокси-класс, сгенерированный с помощью запуска wsdl.exe vim.wsdl vimService.wsdl)
Другими словами, следующая строка кода:
_service = new VimService();
Выполнение может занять около 50 секунд.
ПРИЧИНА
Судя по всему.NET XmlSerializer
использует System.Xml.Serialization.*
атрибуты, аннотирующие прокси-классы для генерации кода сериализации во время выполнения. Когда прокси-классы многочисленны и велики, как, например, код в VimService.cs, генерация кода сериализации может занять много времени.
РЕШЕНИЕ
Это известная проблема с тем, как работает сериализатор Microsoft .NET.
Вот некоторые ссылки, которые MSDN предоставляет для решения этой проблемы:
http://msdn2.microsoft.com/en-us/library/bk3w6240.aspx http://msdn2.microsoft.com/en-us/library/system.xml.serialization.xmlserializerassemblyattribute.aspx
К сожалению, ни одна из вышеупомянутых ссылок не описывает полное решение проблемы. Вместо этого они сосредоточены на том, как предварительно сгенерировать код сериализации XML.
Полное исправление включает в себя следующие шаги:
Создать сборку (DLL) с предварительно сгенерированным кодом XML-сериализатора
Удалите все ссылки на атрибуты System.Xml.Serialization.* Из прокси-кода (т. Е. Из файла VimService.cs).
Аннотируйте основной прокси-класс с помощью атрибута XmlSerializerAssemblyAttribute, чтобы указать, где находится сборка XML-сериализатора.
Пропуск шага 2 приводит только к 20% -ному улучшению времени создания VimService
учебный класс. Пропуск шага 1 или 3 приводит к неправильному коду. За все три этапа достигается улучшение на 98%.
Вот пошаговые инструкции:
Прежде чем начать, убедитесь, что вы используете инструменты.NET версии 2.0. Это решение не будет работать с версией 1.1.NET, потому что инструмент sgen и XmlSerializationAssemblyAttribute
доступны только в версии 2.0.NET
Сгенерируйте файл VimService.cs из WSDL, используя wsdl.exe:
wsdl.exe vim.wsdl vimService.wsdl
Это выведет файл VimService.cs в текущий каталог
Скомпилируйте VimService.cs в библиотеку
csc /t:library /out:VimService.dll VimService.cs
Используйте инструмент sgen для предварительной генерации и компиляции сериализаторов XML:
sgen /p VimService.dll
Это выведет VimService.XmlSerializers.dll в текущий каталог
Вернитесь к файлу VimService.cs и удалите все
System.Xml.Serialization.*
атрибутов. Поскольку код кода большой, лучший способ добиться этого - использовать какой-либо инструмент подстановки регулярных выражений. Будьте осторожны, когда делаете это, потому что не все атрибуты появляются в строке сами по себе. Некоторые из них встроены как часть объявления метода.Если вы считаете этот шаг трудным, вот упрощенный способ сделать это:
Предполагая, что вы пишете на C#, выполните глобальную замену следующей строки:
[System.Xml.Serialization.XmlIncludeAttribute
и заменить его на:
// [System.Xml.Serialization.XmlIncludeAttribute
Это избавит от
Xml.Serialization
атрибуты, которые являются основными виновниками замедления, комментируя их. Если вы используете какой-либо другой язык.NET, просто измените замененную строку, чтобы она была закомментирована с префиксом в соответствии с синтаксисом этого языка. Этот упрощенный подход даст вам большую часть ускорения, которое вы можете получить. Удаление остальных атрибутов Xml.Serialization обеспечивает дополнительное ускорение на 0,2 секунды.Добавьте следующий атрибут в класс VimService в VimService.cs:
[System.Xml.Serialization.XmlSerializerAssemblyAttribute(AssemblyName = "VimService.XmlSerializers")]
Вы должны получить что-то вроде этого:
// ... Some code here ... [System.Xml.Serialization.XmlSerializerAssemblyAttribute(AssemblyName = "VimService.XmlSerializers")] public partial class VimService : System.Web.Services.Protocols.SoapHttpClientProtocol { // ... More code here
Восстановить библиотеку VimSerice.dll
csc /t:library /out:VimService.dll VimService.cs
Теперь из вашего приложения вы можете добавить ссылку на библиотеку VimSerice.dll.
Запустите ваше приложение и убедитесь, что время создания объекта VimService сокращено.
ДОПОЛНИТЕЛЬНЫЕ ПРИМЕЧАНИЯ
Инструмент sgen представляет собой черный ящик, и его поведение зависит от того, что у вас есть в файле Machine.config. Например, по умолчанию предполагается отключить оптимизированный неотладочный код, но это не всегда так. Чтобы получить некоторое представление об инструменте, используйте флаг /k на шаге 3, который заставит его сохранить все свои временные сгенерированные файлы, включая исходные файлы и файлы опций командной строки, которые он сгенерировал.
Даже после вышеуказанного исправления время, необходимое для создания экземпляра класса VimService в первый раз, не является мгновенным (1,5 секунды). Исходя из эмпирических наблюдений, представляется, что большая часть оставшегося времени связана с обработкой SoapDocumentMethodAttribute
атрибутов. На данный момент неясно, как это время может быть сокращено. Предварительно сгенерированная сборка XmlSerializer не учитывает атрибуты, связанные с SOAP, поэтому эти атрибуты должны оставаться в коде. Хорошей новостью является то, что только первое создание экземпляра класса VimService для этого приложения занимает много времени. Таким образом, если дополнительные 1,5 секунды являются проблемой, можно попытаться создать фиктивную реализацию этого класса в начале приложения в качестве средства, улучшающего взаимодействие с пользователем при входе в систему.
Я считаю, что это не проблема SGEN. Я посмотрел на код конструктора и вижу, что он много размышляет (основан на XmlIncludeAttribute в классе). Это отражается на всех них и может занять очень много времени.
Существует предварительно сгенерированная сборка XmlSerializer, которая поставляется с CRM. Проверьте, есть ли в GAC файлы SdkTypeProxy.XmlSerializers.dll и SdkProxy.XmlSerializers.dll.
Если вы этого не сделаете, это означает, что при создании CrmService.net сгенерирует сборку XmlSerializer, что может занять некоторое время. Надеюсь это поможет
Вы можете посмотреть в Sgen.exe
инструмент, который поставляется с.NET. Есть также небольшая полезная вещь в странице свойств сборки проекта Visual Studio C#, в самом низу, называемая "Сборка сборки сериализации", которая автоматически запускается Sgen
для тебя.
Я наткнулся на эту тему, когда пытался выяснить, почему мой начальный SoapHttpClientProtocol
звонки шли так долго.
Я обнаружил, что установка прокси-сервера на ноль / пустое состояние остановила автоматическое обнаружение прокси-сервера - на первоначальный вызов это заняло до 7 секунд:
this.Proxy = GlobalProxySelection.GetEmptyWebProxy();
Я использовал приведенный выше подробный ответ в качестве руководства и сделал несколько шагов вперед, создав сценарий для автоматизации процесса. Скрипт состоит из двух файлов:
generateproxy.bat:
REM if your path for wsdl, csc or sgen is missing, please add it here (it varies from machine to machine)
set PATH=%PATH%;C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools;C:\Program Files (x86)\MSBuild\14.0\Bin
wsdl http://localhost:57237/VIM_WS.asmx?wsdl REM create source code out of WSDL
PowerShell.exe -ExecutionPolicy Bypass -Command "& '%~dpn0.ps1'" REM proces source code (remove annotations, add other annotation, put class into namespace)
csc /t:library /out:references\VIM_Service.dll VIM_WS.cs REM compile source into dll
sgen /p references\VIM_Service.dll /force REM generate serializtion dll
generateproxy.ps1
(Get-Content VIM.cs) |
ForEach-Object {
$_ -replace "(?<attr>\[global::System.Xml.Serialization.[^\]]*\])", "/*${attr}*/" `
-replace "public partial class VIM", "[System.Xml.Serialization.XmlSerializerAssemblyAttribute(AssemblyName = ""VIM_Service.XmlSerializers"")] `npublic partial class VIM" `
-replace "using System;", "namespace Classes.WS_VIM { `n`nusing System;"
} |
Set-Content VIM.cs
Add-Content VIM.cs "`n}"
Я добавил эти два файла в клиентский проект, и в событии перед сборкой я добавил строки
cd..\..
generateproxy
Таким образом, перед каждой сборкой прокси-классы восстанавливаются, и разработчику (почти) не нужно думать об этом. Во время сборки WS должен быть запущен и работать, а его URL должен быть в файле bat. В результате предварительной сборки два dll-файла будут регенерироваться в ссылках подпапок клиентского проекта. После первого выполнения скриптов, вы должны добавить ссылку на новую DLL.