Sellest, et WCF proxy saab using-blokiga halvasti läbi..

On saanud harjumuseks IDisposable tüüpe kasutades kohmaka try-finally bloki asemel ilusat using-süntaksit kasutada. Paraku selgus, et Windows Communication Foundation ei ole selle suhtes väga sõbralikult meelestatud juhul kui teenuse tarbimise käigus tekib mõni viga WCF kanaliga. Näiteks:

using (var usingClient = new FtpProxyServiceClient())
{
    usingClient.RecieveFile(target); //this call throws an exception
}

tagastab võrdlemisi kasutu veateate:

System.ServiceModel.CommunicationObjectFaultedException: The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.

Server stack trace:
   at System.ServiceModel.Channels.CommunicationObject.Close(TimeSpan timeout)

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at System.ServiceModel.ICommunicationObject.Close(TimeSpan timeout)
   at System.ServiceModel.ClientBase`1.System.ServiceModel.ICommunicationObject.Close(TimeSpan timeout)
   at System.ServiceModel.ClientBase`1.Close()
   at System.ServiceModel.ClientBase`1.System.IDisposable.Dispose()
   at SomeNameSpace.UIConsole.Program.Test() in W:\SomePath\UIConsole\Program.cs:line 32

Pöörakem tähelepanu, et vea põhjuse või tekkekoha kohta ei ole jälgegi, vaid viga näib tulevat hoopis Dispose() väljakutsumisel. Selgub, et WCF proxy Close() ebaõnnestub kui eelneva vea tõttu on ühendus juba maha võetud. Tulemuseks on UUS viga ja vana unustatakse hoopis.

Lahenduseks on tagasipöördumine try-finally lahenduse poole ja Abort() kasutamine:

var tryClient = new FtpProxyServiceClient();
Boolean succeeded = false;
try
{
    tryClient.RecieveFile(target); //this call throws an exception
    tryClient.Close();
    succeeded = true;
}
finally
{
    if (!succeeded) { tryClient.Abort(); }
}

Ehk vea järel kutsume Close() asemel välja Abort() meetodi. Selle peale saame lõpuks ometi teada ka algse vea põhjuse, asjaosalise stackframe'i jne:

System.ServiceModel.CommunicationException: The maximum message size quota for incoming messages (20) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element. ---> System.ServiceModel.QuotaExceededException: The maximum message size quota for incoming messages (20) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element.
   --- End of inner exception stack trace ---

[ - skipped some nonrelevant stacktrace entries - ]

   at SomeNameSpace.Ftp.IFtpProxyService.RecieveFile(Uri targetFile)
   at SomeNameSpace.Ftp.FtpProxyServiceClient.RecieveFile(Uri targetFile) in w:\SomePath\ServiceClients.Ftp\Reference.cs:line 102
   at SomeNameSpace.UIConsole.Program.Test()
                 

Kui jõudlus ei ole probleemiks siis funktsionaalselt enam-vähem samaväärne on õnnestumise jälgimine ära jätta ja Abort() alati välja kutsuda. Viga ta ei viska, aga lisandub väike performance overhead ja see oleks ka lihtsalt vale ;)

Abiks oli Damian McGivern'i postitus, kus muuhulgas vihjatakse, kuidas seda try-finally-succeeded blokki mugavamaks teha saaks.

Huvitav, kas c# 4.0 jaoks on WCF proxy Close() pisut koostööaltimaks tehtud ..

Sellest kuidas Skype kaaperdab HTTPS porti..

Tekkis vajadus vahetada IIS6 all aktiivset Website'i. Selline lihtne tegevus, millest ei oskaks probleemi oodata:

a) site A -> Stop
b) site B -> Start

Site A peatati kenasti, IIS aga keeldub Web Site'i B avamast ja kangekaelselt jutustab:

"This process cannot access the file because it is being used by another process"

Informatiivne on veateates kasutada väljendit "the file", kui ÜHEST konkreetsest failist pole juttugi. Õnneks Eventlog on praktilisem ja source HTTP teatab, et :

Unable to bind to the underlying transport for 0.0.0.0:443. The IP Listen-Only list may contain a reference to an interface which may not exist on this machine.  The data field contains the error number.

Selgus, et probleemiks oli Skype, mis leidis omavoliliselt ja alatult, et 443 kuulub talle. Tegelegu oma asjadega (ja võimaldagu admin-inimestel mugavamalt skype pordid kinni keerata kui vaja).

Pärast Skype ajutist mahatapmist sai IIS (ja mina) oma tööd teha.

Sellest, mida System.Decimal kõhus peidab..

Ilmselt iga asjalikum koodikirjutaja teab, mille poolest erinevad System.Single (või float, kui keegi seda rohkem eelistab) ja System.Decimal andmetüübid. Yada-yada, kahendsüsteem vs kümnendsüsteem. Sellegipoolest jäin mõneks hetkeks mõttesse kui avastasin, et ka ToString() käitub erinevalt:

Console.WriteLine((1.0F).ToString());   // returns '1'
Console.WriteLine((1.000F).ToString()); // returns '1'

Console.WriteLine((1M).ToString());     // returns '1'
Console.WriteLine((1.000M).ToString()); // returns '1,000'
Console.WriteLine(1M == 1.000M);        // returns true

Järeldus tuleviku tarbeks on see, et Decimal korral ei tohi eeldada, et ToString() sama väärtusega sisendi korral alati sama vastuse annaks. Kui formaat on oluline, siis tuleb alati see ka täpsustada ning mitte lootma jääda parameetriteta meetodile.

Console.WriteLine((1.000M).ToString("0"));   
// returns '1'

Loomulikult, kui formaat on märgi täpsusega oluline, siis tuleks ka kultuur täpsustada. Antud juhul ei ole see oluline.

 

Miks Decimal nii käitub ?

Teatavasti on  Decimal 128-bitine struktuur. Sisemiselt:

  • 3 * 32bit "täpsusosa" - low, med and  hi-bits
  • märgibitt
  • scale-väärtusele, mis määrab kui suur osa täpsusosa kümnendkohtadest on murdosa.

Siit ka tuleneb lubatud väärtuste hulk:

{ s * c * 10^(-e)|
     s kuulub hulka {-1,1}, 
     0 <= c <= 2^96 ,
     ja 0 <= e <= 28
}

Muuhulgas nähtub, et seetõttu leiduvad erinevad (s,c,e)-komplektid (ehk decimal sisemised väärtustused), mis omavad muutuja kasutaja poolt vaadatuna sama väärtust (näiteks arvu 1). Eelpool kirjeldatud näide demonstreeris just kahte erinevat sisemist väärtustust.

Illustreerimiseks võib selle näite veel ilmekamalt lahti kirjutada, kasutades konstruktorit, mis võimaldab kõiki eeltoodud komponente ise sisestada:

Decimal d10 = new decimal(10, 0, 0, false, 1);
Console.WriteLine(d10.ToString());   // returns '1,0'
Decimal d1000 = new decimal(1000, 0, 0, false, 3);
Console.WriteLine(d1000.ToString()); // returns '1,000'
Console.WriteLine(d10 == d1000);     //returns True;


Aga mille jaoks on ülejäänud bitid ?

Komponendid e ja märgibitt saavad kasutada 128 - 96 = 32 biti jagu ruumi. Kui märgibitt võtab neist tubli 1, siis miks e lubatud väärtused on piiratud 29 erineva väärtusega, kui kasutada on 2^31 ? Vägisi jääb mulje, et 31 - up(log_2 29) = 26 bitti iga Decimali kohta on kasutu ballast.. Raske uskuda, aga mõistlikku selgitust ei näe.


Aga miks System.Single ToString() ikkagi teisiti käitub ?

Teoreetiliselt on ka nende jaoks võimalik konstrueerida samasugune juhtum (1 * 2^10 vs 2 * 2^9) . Pead või sõrmi panti ei paneks, aga oletan, et levinumate floating-point tüüpide korral lahendatakse "normaliseerimine" riistvaras. Kahendsüsteemis nihutamine on ka oluliselt  odavam ettevõtmine.

Sellest, kuidas SQLis kuupäevad keele-sõltumatud hoida..

Seoses avastusega, et ma olen kunagi lohakat koodi kirjutanud ja lubamatult eeldanud, et MSSQL sööb DateTime InvariantCulture väljundit. Sööbki, kui keeled, baasis määratud formaat jms sobivad. Lollikindel on aga kasutada sõltumatut formaati, näiteks ISO8601 (kuju
1999-02-23T22:33:44), mida väljastab näiteks DateTime.ToString("s").

Hea ülevaade teistest võimalustest on toodud siin:
The ultimate guide to the datetime datatypes

Sellest, et nimetamise head tavad on koondunud..

Järjekordne C# koodielementide nimetamise mitteametlik standard: http://www.irritatedvowel.com/Programming/Standards.aspx
Lühidalt, ülevaatlikult, näidetega.

Kui ux-prefix kõrvale jätta, siis ma olen kõigega nõus. Isegi underscore prefixiga (mis mind varem häiris).

Sellest designer-faili sünkroniseerimisveast..

Juhtus sedamoodi, et kirjutades aktiivselt ühe ascx märgendeid, avastasin koodiaknasse pöördudes, et intellisense ei paku mulle äsja markup'is kirjeldatud elemente. Kiire pilk ascx.designer.cs klassi kinnitas - pooled elemendid on sealt puudu. Salvestamisel ei mingit viga.

Selgus, et selle asemel et runtime'is viga püüda kätte saada, on olemas lihtsam viis: kustuta designer fail ära ja vali ascx pealt "Convert to web application". Selle peale ütleb ta ilusa veateate mis teda sünkroniseerimast takistab.

.. ja tagasi markup'i.

Sellest kuidas embedded JS UI projektist kätte saada..

WebResource on äärmiselt mugav vahend javascripti, Css'i, pilte jms oma kompileeritud assembly'ga kaasa panna. Ilus ja lihtne. Lühikirjeldus näiteks siin:
http://www.aspcode.net/Including-WebResource-in-ASPNET-server-control.aspx

Seda häirivam on avastamine, et nii lihtne asi ei tööta miskipärast. mitmed ajad tänasest päevast debugisin ühte Javascripti faili, mida ma soovisin oma UserControli sees tarbida, kuid mis kangekaelselt keeldus kättesaadav olemast.

UI projekt, fail kaustas: /JS/Base.js

AssemblyInfo.cs:

[assembly: WebResource("SomeProject.JS.Base.js", "text/javascript")]

Myfile.cs:

var scriptManager = this.Page.ClientScript;
if (!scriptManager.IsClientScriptIncludeRegistered(JavascriptResourceName))
{
     scriptManager.RegisterClientScriptInclude(JavascriptResourceName,
     scriptManager.GetWebResourceUrl(this.GetType(), JavascriptResourceName));
}


Kutsudes välja javasscripti meetodit sellest failist, kurdetakse mulle, et sellist ei leita..

debug sessioon:
1) Kas dll'is on JS-fail olemas ? Vaatasin reflectoriga, olemas.
2) Kas scriptmanager lisas script-viite html'i ? Jah, olemas.
3) kas javascript on html'ist võetavalt URLilt kättesaadav ? ja sain oma EI!

Tuleb tunnistada, et see tekitas hämmeldust. Kuhu see kaduda sai?

Autentimise probleem ? Debugisin - Webresource päring annab kenasti cookie kaasa ja autenditakse probleemita.

..

Ja siis laekus: scriptManager genereerib vale URL'i! sest this.GetType() on ju genereeritud _ascx- klassis, mis ei paikne mitte samas Assembly's kus JS. Üllatustevabalt Quckfix töötas nagu planeeritud:

var scriptManager = this.Page.ClientScript;
if (!scriptManager.IsClientScriptIncludeRegistered(JavascriptResourceName))
{
    scriptManager.RegisterClientScriptInclude(JavascriptResourceName,
        scriptManager.GetWebResourceUrl(typeof(AddressRow), JavascriptResourceName));
}

Sellest kuidas saada VS Setup projekti alla puuduvad installer-pakke..

Tavalisi asju nagu erinevaid .NET Framework versioone, Reportviewer, Crystal jms. Abiks on Bootstrapper Manifest Generator mis vimaldab muuhulgas valida võimalikke pakke ja neid veebist masinasse õigesse kausta alla laadida.

Dowload ja muu jutt on leitav: http://code.msdn.microsoft.com/bmg

Sellest, et Asp.Net ei anna workeritele liigap alju mälu..

Veateade:

aspnet_wp.exe  (PID: 4408) was recycled because memory consumption exceeded the 1227 MB (60 percent of available RAM).

IIS 6.0 korral  (näiteks Windows Server 2003) saab workerile lubatud mälu sättida IIS settingute juurest:
AppPool->Properties->tab Recycling-> blokk "memory recycling".

IIS 5.1 korral (Windows XP) aga ei ole Application Pool'i konfigureerimine nii lihtne. Abiks on machine.config täiendamine:

<system.web>
  ...
  <processModel autoConfig="true" memoryLimit="80"/>
  ...
</system.web>
See annab näiteks kasutada 80% masina RAMist.

Sellest, kuidas Winforms rakenduses VS genereeritud koodi partial failidesse saada..

Microsoft .Net 2.0 üs meeldivamaid featuure (nullable tüpide kõrval loomulikult) on partial võtmesõna ja sellega seonduv. See võimaldab kenasti geneereeritud koodi ja käsitsikirjutatud koodi lahus hoida, millega kaasaegne VS (näiteks 2008) ka kenasti hakkama saab. Paraku vanad rakendused, mis on loodud .Net Framework 1.1 ajal hoiavad kõike seda ühes failis. Proovige teinekord ülekäteläinud 6000 reaga failist loogika muudatust otsida. Diff ei ole just ideaalne lahendus ;)

ASP.NET rakenduste korral on projekti failidele designer-partial klassi loomine lihtne. VS ise pakub selleks projekti hüpikmenüüs käsku "Convert to Web Application" vms. Winforms korral see võimalus aga puudub. Google oli armuline ja andis selleks puhuks mulle lingi http://coolsoft.altervista.org/DeCodEx

Ehk väike programm, mis minu põgusa testimise tulemusena teeb monoliitsed vormifailid üle valitud VS projekti kenasti partial tüüpide peale.