Domanda

Sto facendo alcuni esperimenti con Microsoft Dynamics CRM. Interagisci con esso attraverso i servizi web e ho aggiunto un riferimento Web al mio progetto. L'interfaccia del servizio Web è molto ricca e il generato "Reference.cs" è circa 90k loc.

Sto usando il riferimento web in un'applicazione console. Cambio spesso qualcosa, ricompilo ed eseguo. La compilazione è veloce, ma il rinnovo del riferimento del servizio Web è molto lento, impiegando circa 15-20 secondi:     Servizio CrmService = new CrmService (); La profilatura rivela che tutto il tempo è trascorso nel costruttore SoapHttpClientProtocol.

Apparentemente il colpevole è il fatto che il codice di serializzazione XML (non incluso nella loc 90k menzionata sopra) viene generato in fase di esecuzione, prima di essere JIT. Questo accade durante la chiamata del costruttore. L'attesa è piuttosto frustrante quando si gioca e si provano le cose.

Ho provato varie combinazioni di sgen.exe, ngen e XGenPlus (che richiede diverse ore e genera 500 MB di codice aggiuntivo) ma senza risultati. Ho preso in considerazione l'implementazione di un servizio Windows che ha poche istanze di CrmService pronte per essere fornite quando necessario ma che sembrano eccessive.

Qualche idea?

È stato utile?

Soluzione

Il seguente testo viene strappato da questo thread sui forum VMWare:

Ciao gente

Abbiamo scoperto che sgen.exe funziona. È giusto che ci siano un paio di passaggi aggiuntivi oltre alla pre-generazione delle DLL del serializzatore che abbiamo perso in questo thread. Ecco le istruzioni dettagliate

PROBLEMA

Quando si utilizza VIM 2.0 SDK da .NET richiede molto tempo per creare un'istanza della classe VimService. (La classe VimService è la classe proxy generata eseguendo 'wsdl.exe vim.wsdl vimService.wsdl')

In altre parole, la seguente riga di codice:

_service = new VimService();

L'esecuzione potrebbe richiedere circa 50 secondi.

CAUSA

Apparentemente, .NET XmlSerializer utilizza gli attributi System.Xml.Serialization. * che annotano le classi proxy per generare il codice di serializzazione in fase di esecuzione. Quando le classi proxy sono molte e grandi, come il codice in VimService.cs, la generazione del codice di serializzazione può richiedere molto tempo.

SOLUTION

Questo è un problema noto con il funzionamento del serializzatore Microsoft .NET.

Ecco alcuni riferimenti forniti da MSDN sulla risoluzione di questo problema:

http://msdn2.microsoft.com/en-us/library/ bk3w6240.aspx http://msdn2.microsoft.com/en-us /library/system.xml.serialization.xmlserializerassemblyattribute.aspx

Sfortunatamente, nessuno dei riferimenti sopra descritti descrive la soluzione completa al problema. Si concentrano invece su come pre-generare il codice di serializzazione XML.

La correzione completa prevede i seguenti passaggi:

  1. Crea un assembly (una DLL) con il codice serializzatore XML pre-generato

  2. Rimuovi tutti i riferimenti agli attributi System.Xml.Serialization. * dal codice proxy (ovvero dal file VimService.cs)

  3. Annota la classe proxy principale con XmlSerializerAssemblyAttribute per indicarlo dove si trova l'assembly del serializzatore XML.

Saltando il passaggio 2 si ottiene solo un miglioramento del 20% nel tempo di istanza per la classe VimService . Saltare il passaggio 1 o 3 porta a un codice errato. Con tutti e tre i passaggi si ottiene un miglioramento del 98%.

Ecco le istruzioni dettagliate:

Prima di iniziare, assicurati di utilizzare gli strumenti di .NET versione 2.0. Questa soluzione non funzionerà con la versione 1.1 di .NET perché lo strumento sgen e XmlSerializationAssemblyAttribute sono disponibili solo nella versione 2.0 di .NET

  1. Genera il file VimService.cs dal WSDL, usando wsdl.exe:

    wsdl.exe vim.wsdl vimService.wsdl

    Questo genererà il file VimService.cs nella directory corrente

  2. Compila VimService.cs in una libreria

    csc / t: library /out:VimService.dll VimService.cs

  3. Utilizza lo strumento sgen per pre-generare e compilare i serializzatori XML:

    sgen / p VimService.dll

    Questo produrrà VimService.XmlSerializers.dll nella directory corrente

  4. Torna al file VimService.cs e rimuovi tutti gli attributi System.Xml.Serialization. * . Poiché il codice del codice è grande, il modo migliore per ottenerlo è utilizzare uno strumento di sostituzione delle espressioni regolari. Fai attenzione mentre lo fai perché non tutti gli attributi appaiono su una linea da soli. Alcuni sono allineati come parte di una dichiarazione del metodo.

    Se trovi difficile questo passaggio, ecco un modo semplificato per farlo:

    Supponendo che tu stia scrivendo C #, esegui una sostituzione globale sulla seguente stringa:

    [System.Xml.Serialization.XmlIncludeAttribute

    e sostituiscilo con:

    // [System.Xml.Serialization.XmlIncludeAttribute

    Questo eliminerà gli attributi Xml.Serialization che sono i maggiori colpevoli del rallentamento commentandoli. Se stai usando un altro linguaggio .NET, modifica semplicemente la stringa sostituita per essere commentata come prefisso in base alla sintassi di quel linguaggio. Questo approccio semplificato ti consentirà di ottenere la maggior velocità possibile. La rimozione del resto degli attributi Xml.Serialization consente di ottenere solo una velocità extra di 0,2 secondi.

  5. Aggiungi il seguente attributo alla classe VimService in VimService.cs:

    [System.Xml.Serialization.XmlSerializerAssemblyAttribute (AssemblyName = " VimService.XmlSerializers ")]

    Dovresti finire con qualcosa del genere:

    // ... Alcuni codici qui ... [System.Xml.Serialization.XmlSerializerAssemblyAttribute (AssemblyName = " VimService.XmlSerializers ")] classe parziale pubblica VimService: System.Web.Services.Protocols.SoapHttpClientProtocol { // ... Altro codice qui

  6. Rigenera la libreria VimSerice.dll di

    csc / t: library /out:VimService.dll VimService.cs

  7. Ora, dalla tua applicazione, puoi aggiungere un riferimento alla libreria VimSerice.dll.

  8. Esegui l'applicazione e verifica che il tempo di instanciamento dell'oggetto VimService sia ridotto.

NOTE AGGIUNTIVE

Lo strumento sgen è un po 'una scatola nera e il suo comportamento varia a seconda di ciò che hai nel tuo file Machine.config. Ad esempio, per impostazione predefinita dovrebbe estrapolare il codice non di debug ottimizzato, ma non è sempre così. Per ottenere un po 'di visibilità sullo strumento, utilizzare il flag / k al passaggio 3, che lo farà conservare tutti i suoi file temporanei generati, inclusi i file di origine e i file delle opzioni della riga di comando che ha generato.

Anche dopo la correzione di cui sopra il tempo necessario per creare un'istanza della classe VimService per la prima volta non è istantaneo (1,5 sec). Sulla base di osservazioni empiriche, sembra che la maggior parte del tempo rimanente sia dovuto all'elaborazione degli attributi SoapDocumentMethodAttribute . A questo punto non è chiaro come questo tempo possa essere ridotto. L'assembly XmlSerializer pre-generato non tiene conto degli attributi relativi a SOAP, quindi questi attributi devono rimanere nel codice. La buona notizia è che solo la prima istanza della classe VimService per quell'app richiede molto tempo. Quindi, se gli ulteriori 1,5 secondi sono un problema, si potrebbe provare a fare un'istanza fittizia di questa classe all'inizio dell'applicazione come mezzo per migliorare l'esperienza utente del tempo di accesso.

Altri suggerimenti

Potresti voler esaminare Sgen.exe strumento fornito con .NET. C'è anche una piccola cosa utile nelle proprietà del progetto C # di Visual Studio " Build " pagina, in fondo, chiamata " Build assembly serialization " che esegue automaticamente Sgen per te.

Credo che questo non sia un problema SGEN. Ho esaminato il codice del costruttore e vedo che sta facendo molte riflessioni (basato su XmlIncludeAttribute sulla classe). Riflette su tutti loro e può richiedere molto tempo.

Esiste un assembly XmlSerializer pre-generato fornito con CRM. Verificare se nel GAC sono presenti SdkTypeProxy.XmlSerializers.dll e SdkProxy.XmlSerializers.dll.

In caso contrario, ciò significa che quando si crea CrmService, .net genererà l'assembly XmlSerializer che può richiedere del tempo. Spero che questo aiuti

Mi sono imbattuto in questo thread nel tentativo di scoprire perché le mie chiamate iniziali SoapHttpClientProtocol stavano impiegando così tanto tempo.

Ho scoperto che l'impostazione del proxy su null / vuoto ha impedito il verificarsi del rilevamento automatico del proxy. La chiamata iniziale impiegava fino a 7 secondi:

this.Proxy = GlobalProxySelection.GetEmptyWebProxy();

Ho usato la risposta dettagliata sopra come guida e ho fatto alcuni passi avanti, realizzando uno script per automatizzare il processo. Lo script è composto da due file:

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 { 
        

Ho usato la risposta dettagliata sopra come guida e ho fatto alcuni passi avanti, realizzando uno script per automatizzare il processo. Lo script è composto da due file:

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

cd..\..
generateproxy

Ho aggiunto questi due file al progetto client e nell'evento pre-build ho aggiunto delle linee

<*>

Quindi, prima di ogni build, le classi proxy vengono rigenerate e lo sviluppatore non ha (quasi) bisogno di pensarci. Durante la creazione, WS deve essere attivo e in esecuzione e il suo URL deve essere in file bat. Come risultato del pre-build, due file DLL verranno rigenerati nella sottocartella del progetto client riferimenti . Dopo la prima esecuzione degli script, è necessario aggiungere un riferimento alla nuova dll.

-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}"

Ho aggiunto questi due file al progetto client e nell'evento pre-build ho aggiunto delle linee

<*>

Quindi, prima di ogni build, le classi proxy vengono rigenerate e lo sviluppatore non ha (quasi) bisogno di pensarci. Durante la creazione, WS deve essere attivo e in esecuzione e il suo URL deve essere in file bat. Come risultato del pre-build, due file DLL verranno rigenerati nella sottocartella del progetto client riferimenti . Dopo la prima esecuzione degli script, è necessario aggiungere un riferimento alla nuova dll.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top