Подтвердить что ты не робот

Конструктор медленного SoapHttpClientProtocol

Я делаю некоторые эксперименты с Microsoft Dynamics CRM. Вы взаимодействуете с ним через веб-службы, и я добавил веб-ссылку на мой проект. Интерфейс веб-сервиса очень богат, а сгенерированный "Reference.cs" - это около 90 тыс. Loc.

Я использую веб-ссылку в консольном приложении. Я часто что-то меняю, перекомпилирую и бегу. Компиляция выполняется быстро, но новинка в ссылке на веб-службу очень медленная, занимает около 15-20 секунд: CrmService service = new CrmService(); Профилирование показывает, что все время тратится на конструктор SoapHttpClientProtocol.

Виной является, по-видимому, тот факт, что код сериализации XML (не включенный в 90k loc, упомянутый выше) генерируется во время выполнения, прежде чем быть JIT'ed. Это происходит во время вызова конструктора. Ожидание довольно неприятно, когда вы играете и пытаетесь.

Я пробовал различные комбинации sgen.exe, ngen и XGenPlus (который занимает несколько часов и генерирует 500 Мбайт дополнительного кода), но безрезультатно. Я рассмотрел возможность внедрения службы Windows, в которой есть несколько экземпляров CrmService, готовых к раздаче, когда это необходимо, но это кажется чрезмерным.

Любые идеи?

4b9b3361

Ответ 1

Из раздела this на форумах VMWare:

Привет, люди,

Мы обнаружили, что sgen.exe действительно работает. Это просто пара дополнительных шагов, помимо предварительного генерации dll serializer, которые мы пропустили в этом потоке. Вот подробная инструкция

ПРОБЛЕМА

При использовании 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 verison 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.*. Поскольку кодовый код является большим, лучшим способом добиться этого является использование некоторого инструмента замещения регулярных выражений. Будьте осторожны, так как вы делаете это, потому что не все атрибуты появляются на отдельной строке. Некоторые из них выложены как часть декларации метода.

    Если вы обнаружите, что этот шаг затруднен, это упрощенный способ сделать это:

    Предполагая, что вы пишете С#, выполните глобальную замену в следующей строке:

    [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 секунды являются проблемой, можно попытаться создать фиктивный экземпляр этого класса в начале приложения в качестве средства улучшения пользовательского времени входа в систему.

Ответ 2

Возможно, вы захотите изучить инструмент Sgen.exe, который поставляется с .NET. Там также есть небольшая вещь в свойствах проекта Project Studio С# проекта "Build", в самом низу, которая называется "Build serialization assembly", которая автоматически запускает Sgen для вас.

Ответ 3

Я считаю, что это не проблема SGEN. Я посмотрел на код конструктора, и вижу, что он много размышляет (на основе XmlIncludeAttribute в классе). Он отражает их все и может занять очень много времени.

Ответ 4

Существует предварительно сгенерированная сборка XmlSerializer, которая поставляется с CRM. Проверьте, есть ли у вас SdkTypeProxy.XmlSerializers.dll и SdkProxy.XmlSerializers.dll в GAC.

Если вы этого не сделаете, это означает, что при создании CrmService.net создаст сборку XmlSerializer, которая может занять некоторое время. Надеюсь, что это поможет

Ответ 5

Я столкнулся с этим вопросом, пытаясь выяснить, почему мои начальные вызовы SoapHttpClientProtocol занимали так много времени.

Я обнаружил, что установка прокси на null/Empty остановила прокси-аудит AutoDetect - это заняло до 7 секунд при первоначальном вызове:

this.Proxy = GlobalProxySelection.GetEmptyWebProxy();

Ответ 6

Я использовал выше подробный ответ в качестве руководства и сделал несколько шагов вперед, создав script для автоматизации процесса. script состоит из двух файлов:

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.