Normalt forventer man, at hvis a + b giver c, så vil a + b altid give c.  I programmering skal man have en vis tolerance mht. præcision, når man bruger doubles og floats, men hvis a + b giver c i et program, forventer man samme resultat, uanset hvornår man laver beregningen indenfor samme proces.

Sådan troede jeg det var indtil fornylig, hvor én af vores unit tests fejlede – tilsyneladende lidt tilfældigt.  For kørte jeg den pågældende unit test alene, var der ingen fejl.  Kørte jeg den sammen med de andre unit tests i projektet, fejlede den.  Jeg fik ret hurtigt identificeret, hvilke andre unit tests, der drillede, og den fejlende unit test havde absolut intet med de andre unit tests at gøre.  Der var ingen fælles kode.

For nemheds skyld vil jeg kalde den fejlende unit test for A, og kalde de andre unit tests, som forårsagede fejlen i A for B. 

Test A tester kode indeholdende komplekse beregninger (Nelder-Mead), og fejlen gjorde, at det endelige resultat endte med en difference på 0.5!  Det er meget i min verden.

Test B er lidt speciel.  Den tester funktionalitet til at indlæse data fra et regneark.  Dvs. koden, som B tester, benytter Microsoft’s ACE driver. Efter en del test af denne driver, og noget der ligner to dages riven i mit eget hår, var jeg i stand til at genskabe problematikken med et simpelt eksempel, hvor resultat af et gangestykke med doubles gave forskellige resultater før og efter kald til ACE driveren.

Et spørgsmål på StackOverflow affødte en forklaring: “… unmanaged code may be tinkering with the FPU control word and change the way it calculates”.  Der blev også foreslået en løsning, nemlig et kald til _fpreset, som “resets the floating point package”.

Den foreslåede løsning virker, men jeg føler mig ikke overbevist om, at jeg egentlig har lyst til at bruge ACE driveren direkte i vores produktionskode.  En rådslagning med Daniel (også kendt er på sitet som ne0san) førte frem til en anden løsning, nemlig at spawn en ny process, som indlæser data fra regnearket, og kommunikerer data tilbage til hovedprocessen vha. named pipes (måske et emne for en kommende dotninjas blog).  Det virker.  Om det er en bedre løsning end _fpreset er svært at sige.

Betragt følgende stump SQL og overvej resultatet:

SET ANSI_NULLS ON;
SELECT 
CASE 
    WHEN NOT 'EnStreng' IN ('A', NULL) THEN 'Ja'
    ELSE 'Nej'
END

Hvis du gættede på, at resultatet ville blive ‘Nej’, er du klogere end mig (hvilket mange er).  Sagen er, at med ANSI_NULL ON, vil alle sammenligninger med NULL returnere værdien UNKNOWN, og alle boolean operatorer vil returnere UNKNOWN, når de bruges sammen med UNKNOWN.  Derfor vil første case aldrig evaluere til TRUE.

Som hovedregel må man aldrig bruge =, < og > ved sammenligninger med NULL, når ANSI_NULL er ON.

For fremtidige versioner af SQL Server (hvad så end det betyder) vil ANSI_NULLS altid være ON.  Mere info her: http://msdn.microsoft.com/en-us/library/ms188048(v=sql.110).aspx

 

For et par dage siden modtog jeg to forskellige stylus'er, som var blevet bestilt fra dealextreme som et hurtigt impulskøb. Det er første gang, jeg bestiller fra dx.com, så jeg var lidt spændt på om jeg måtte bløde en masse, da pakken blev sendt fra Kina. Det var heldigvis ikke tilfældet, så jeg måtte alt i alt slippe i omegnen af 100kr. for 6 stylus'er.

Jeg valgte at satse på to forskellige modeller for at sprede risikoen lidt, og valgte to, der havde fået relative gode anmeldelser fra andre brugere på dx.com. De to modeller er:

Der er blevet leget lidt med de to modeller i gratis-versionen af GoodNotes, der er én af mange notattagningsprogrammer, der findes til ipad. Da jeg ikke længere er studerende er GoodNotes og stylus'er ikke blevet testet i "rigtige" scenarier, og jeg skal ikke kunne sige, om det er bedre at kaste 20-70kr. efter nogle af de andre apps på markedet. GoodNotes har dog de features jeg umiddelbart kunne forestille mig ville være relevante for mig, blandt andet:

  • Skrivefelt. Når man skriver med en stylus er det svært at skrive lille skrift, da stylus'er som regel har en stor rund og blød ende. Derfor svarer det lidt til at skrive med tavlekridt. Skrivefeltet sørger for, at din store håndskrift nedskaleres til "normal" tekststørrelse på dit "kladdepapir". Se Zoom window nederst på denne side for et eksempel.
  • Import af PDF. Smart hvis man skal kommentere og korrekturlæse på et dokument. Jeg er stadig bedst til at læse korrektur med et stykke papir i hånden og en rød kuglepen i hånden, og på denne måde kan jeg næsten få samme følelse som med papir.
  • Import af billeder. Jeg forestiller mig det kan være anvendeligt i nogle sammenhængen. Det fungerer i hvert fald upåklageligt.
  • Figurtegning. Når denne funktion er slået til vil app'en gøre en streg helt lige, en cirkel rund etc. Det gør det noget lettere at lave pæne figurer, men man skal nok lige vænne sig lidt til funktionaliteten.
  • Farver, stregtykkelse og overstregninger. Disse features må forventes at være minimumsfunktionalitet i sådan en app.

Jeg har ikke noget at udsætte på GoodNotes. Det fungerer rigtigt godt og den eneste begrænsning i den gratis version er et maksimum på 2 noter.

De to stylus'er ligner hinanden i udseende og materialer. De er begge sorte og lavet af plast. Aluminum Alloy er lidt kortere end en normal kuglepen mens den anden er samme størrelse som en normal kuglepen, og indeholder også en rigtig kuglepen (som jeg ikke har afprøvet).

Black Ink Ball Pen har en blød ende mens Aluminum Alloy er lidt mere hård i gummiet, hvorfor man skal presse lidt mere hårdt for at aktivere stulus'en mod glasset på ipad'en. Personligt foretrækker jeg Black Ink Ball Pen da man ikke behøver at presse så hårdt og samtidigt passer størrelsen mig lidt bedre, da den føles som en almindelig kuglepen i hånden.

Priserne taget i betragtning er det bare at bestille nogle stykker hjem - holdbarheden er formodentlig relativ lav og holder næppe til at man får sat sig på dem i sofaen. Til gengæld får man til billige penge en masse ekstra funktionalitet til sin tablet.

Forleden blev jeg mindet om SQLCMD Mode i SQL Server Management Studio.  Jeg brugte det flittigt i en periode for nogle år tilbage, men er af en eller anden grund kommet væk fra det.  SQLCMD Mode gør det muligt at kalde sqlcmd.exe direkte i sine SQL scripts i SSMS, og det kan være ganske nyttigt. 

Opsætning

For at slå SQLCMD Mode til, skal man trykke på knappen SQLCMD Mode i værktøjslinjen i SSMS.  Som standard er denne knap ikke synlig.  For at tilføje den skal man gå ind under Tools –> Customize menuen.  Under fanen Commands skal man trykke Add Command og finde knappen under Query kategorien.  Man kan også bruge menuen Query –> SQLCMD Mode.

image

Når SQLCMD Mode er slået til, kan man fra SSMS eksekvere SQLCMD kommander ved at skrive et kolon (‘:’) foran kommandoen.  SQLCMD kommandoer fremhæves som grå linjer i SSMS. 

Ændre forbindelse til SQL Server

En af de gode kommandoer er :Connect.  Med :Connect kan man forbinde til en SQL Server direkte fra sit SQL script:

image

Det betyder, at man kan gemme forbindelsesinformation direkte i sine SQL filer.

Variable

En anden nyttig funktion er muligheden for at definere variable. I nedenstående eksempel benytter jeg det til hurtigt at generere noget testdata.  Jeg har defineret en tabel kaldet Table_1, der har default værdier på alle kolonner.  Vha. SQLCMD’s mulighed for at definere variable og SSMS’ definition af GO, kan jeg eksekvere min INSERT statement 10 gange:

image

Eksekvering af cmd prompt kommandoer

SQLCMD kan selvfølgelig også andre sjove ting.  F.eks. kan man eksekvere almindelige cmd prompt kommandoer vha. ‘!!’, hvis man skulle få lyst til det:

image

Standardopsætning

Hvis man som standard ønsker at åbne alle SQL scripts i SQLCMD Mode i SSMS, skal man gå ind under Tools –> Options og tilvælge det:

image

Der er sagt og skrevet mange dårlige ting om Microsoft Surface RT.  Jeg har selv haft en RT siden den kom til Danmark, og jeg er ikke enig med den negative kritik, Surface har fået.  Generelt mener jeg, at mange mennesker rakker ned på Microsoft produkter, bare fordi det er moderne.  Jeg går hermed i mod strømmen.

I mine øjne er Surface en tablet computer og dermed en konkurrent til iPad.  Man bør ikke forvente mere af en Surface end af en iPad, og det er måske der, folk misforstår Surface.  Jeg tror ikke, at Surface vil slå iPad.  Men det burde den, og her er 5 (fuldstændig subjektive) grunde til det (i tilfældig rækkefølge):

  1. Split screen.  Idéen med at vise to apps samtidig, med et 1/3 – 2/3 split er genial.  Jeg har f.eks. ofte Skype chat kørende i siden, mens jeg sidder og læser.  Eller f.eks. har jeg Bing Search i siden, mens jeg surfer i Internet Explorer.  Når man klikker på et søgeresultat i Bing Search, vises web siden i IE.  Det kan man ikke engang gøre på en desktop computer!
    Skærmbillede (5)
  2. Touch Cover. Det er virkelig lækkert med et (næsten) full size tastatur, som endda fungerer som cover.  Man skal vænne sig til det, men det er muligt at skrive med næsten fuld hastighed med 10-fingersystem.
  3. Store/små bogstaver på onscreen-keyboard.  På Surface kan man tydeligt se, om man har shift trykket ned, for onscreen-keyboard skifter mellem store og små bogstaver.  Det er en meget lille detalje, men det irriterer mig, at iPad/iPhone ikke gør det.
    Skærmbillede (8)Skærmbillede (7)
  4. Live tiles.  Nogle bliver forvirrede over dem – jeg elsker dem.  Man kan med det samme se, hvem seneste mails er fra, næste kalenderaftale, seneste nyheder, dagens udvikling på C20-indekset, Facebook-opdateringer fra sine søstre, chat-beskeder på Skype osv. osv.  Live tiles er et lækkert alternativ til iPad’s ikoner, der står passive og nærmest skriger til hinanden efter opmærksomhed.
    image
  5. USB-stik.  Det lille USB-stik i siden kombineret med Windows’ væld af drivers gør en verden til forskel.  Sæt dit 5 år gamle Nikon kamera i uden brug af adapter eller noget og overfør (RAW) billederne uden problemer.  Sæt din 4 år gamle Brother laser printer til 800 kr. i og print uden problemer.  Sæt din Windows Phone telefon i og overfør musik og billeder uden problemer.  Jeg har ikke oplevet nogensomhelst problemer med at skulle sætte en disk i A-drevet eller andet, som f.eks. det skete for So Ein Ding.  Alt har virket i første hug for mig.
  6. (ok, jeg løj, der er mere end 5 grunde) Flash.  Det er vel efterhånden kun Danske Spil tilbage, som bruger Flash, men engang imellem er det nyttigt at sidde med en maskine, der kan vise Flash.  Og det kan Surface.
  7. Mini-SD port.  Jeg har endnu ikke haft brug for den, men det er en rar fornemmelse, at den er der.
  8. App-skift med et swipe.  Swipe én gang med venstre tommeltot for at skifte til forrige app.  Swipe en gang ud og ind og se hele listen af kørende apps.  Det er utroligt nemt og intuitivt.
    Skærmbillede (12)
  9. Charms.  Charms er de små ikoner, der kommer frem, når man swiper med højre tommeltot.  Uanset hvilken app, man kører, kan man her finde app’ens indstillinger og muligheder for deling (via Facebook, Skydrive, mail etc).  Det er godt tænkt, fordi det giver konsistens gennem alle apps.  Faktisk er hele konceptet med at bruge tommeltotterne til at styre Surface god.
    Skærmbillede (11)
  10. Flere brugere.  En Surface kan have flere logins.  Slut med at have konens baggrundsbillede af havens pæoner på sin tablet.

Som det måske fremgår, er jeg rigtig glad for Surface.  Det betyder ikke, at den er perfekt.  Jeg håber, at Microsoft en dag kommer med en RT version af Office, så desktoppen helt kan fjernes fra RT.  Der er kommet en udmærket OneNote app, men de andre Office produkter eksekveres på desktoppen, og det er en skam. 

Jeg håber også, at firmaer og udviklere verden over får øjnene op for Surface, så udbuddet af apps bliver større.

Og jeg håber, Microsoft vil gøre noget ved vægten på Surface.  Jeg vil skyde på, at den vejer ca. det samme som en iPad 1.

Det er en god og velkendt tommelfingerregel, at man skal placere et fotografis emne eller “action” 1/3 inde i billedet.  Denne regel er kendt som “Rule of Thirds”.  Det gælder både vertikalt og horisontalt. 

Selvom man ikke kender reglen, vil mange automatisk alligevel bruge den, når de fotograferer, for på en eller anden måde virker det harmonisk for mennesker (og sikkert også for chimpanser, siden vi nu har så meget til fælles med dem).

Digitalkameraproducenter er så flinke at give fotografer mulighed for at få vist guidelines inde i kameraets viewfinder for at hjælpe med at holde kameraet lige etc.  Siden Rule of Thirds er en universiel regel for fotografer, kan jeg simpelthen ikke begribe, hvor de guidelines i mit kamera benytter 4. dele i stedet for 3. dele.  Det ville da gøre livet nemmere.

Sådan ser min viewfinder ud.  Har de brugt en helt ny endnu ukendt regel kaldet Rule of Fourths?

d80_viewfinder_anim.gif

Da async/await pattern blev introduceret med C# 5, blev hele .NET frameworket gennemarbejdet, så relevante asynkrone metoder blev awaitable og fik Task som returværdi.

Det gælder ikke Windows Phone API’en.  Specielt gælder det ikke for WebClient, der i .NET 4.5 bl.a. har fået DownloadDataTaskAsync, der returnerer en Task, og dermed er awaitable.  Til Silverlight må man nøjes med DownloadDataAsync, som er baseret på Event-Based Asynchronous Pattern (EAP) gennem DownloadDataCompleted.

I System.Threading.Tasks namespacet findes en klasse kaldet TaskCompletionSource.  Denne klasse er netop beregnet til EAP scenarier, fordi den opretter en task, som man kan sætte op til at vente på, at en event bliver kaldt.  Dette gøres vha. af SetResult og SetException.  Kalder man en af de to metoder, vil den tilhørende Task afsluttes.  Vha. af TaskCompletionSource har jeg lavet følgende awaitable extension metoder til WebClient, som gør det muligt at lave awaitables for hver af de 4 HTTP metoder GET, POST, PUT og DELETE.  Metoderne benytter JSON via Json.NET:

    public static class WebClientExtensions
    {
        public static Task<T> GetAsync<T>(this WebClient client, Uri uri)
        {
            var tcs = new TaskCompletionSource<T>();
            client.DownloadStringCompleted += (s, e) =>
                {
                    if (e.Error == null)
                    {
                        T result = JsonConvert.DeserializeObject<T>(e.Result);
                        tcs.SetResult(result);
                    }
                    else
                    {
                        tcs.SetException(e.Error);
                    }
                };
            client.DownloadStringAsync(uri);
            return tcs.Task;
        }

        public static Task PostAsync<T>(this WebClient client, Uri uri, T item)
        {
            var tcs = new TaskCompletionSource<string>();
            client.Headers["Content-Type"] = "application/json";
            client.UploadStringCompleted += (s, e) =>
            {
                if (e.Error == null)
                {
                    tcs.SetResult(e.Result);
                }
                else
                {
                    tcs.SetException(e.Error);
                }
            };

            string data = JsonConvert.SerializeObject(item);

            client.UploadStringAsync(uri, "POST", data);
            return tcs.Task;
        }

        public static Task PutAsync<T>(this WebClient client, Uri uri, T item)
        {
            var tcs = new TaskCompletionSource<string>();
            client.Headers["Content-Type"] = "application/json";
            client.UploadStringCompleted += (s, e) =>
            {
                if (e.Error == null)
                {
                    tcs.SetResult(e.Result);
                }
                else
                {
                    tcs.SetException(e.Error);
                }
            };

            string data = JsonConvert.SerializeObject(item);

            client.UploadStringAsync(uri, "PUT", data);
            return tcs.Task;
        }

        public static Task DeleteAsync(this WebClient client, Uri uri)
        {
            var tcs = new TaskCompletionSource<string>();
            client.UploadStringCompleted += (s, e) =>
            {
                if (e.Error == null)
                {
                    tcs.SetResult(e.Result);
                }
                else
                {
                    tcs.SetException(e.Error);
                }
            };

            client.UploadStringAsync(uri, "DELETE", "");
            return tcs.Task;
        }

Du kan læse mere om TaskCompletionSource hos Stephen Toub, som er en af de ypperste, når det kommer til parallelisering i .NET.

Jeg har haft stor glæde af de ovenstående WebClient extensions, men min glæde var endnu større, da jeg fornylig læste, at Microsoft arbejder på en port af HttpClient til Windows Phone, og har gjort det tilgængeligt via NuGet.  Jeg har endnu ikke prøvet det, men det virker lovende.

Jeg læste forleden om dårlige vaner, man typisk har som (amatør)fotograf.  Jeg kan nikke til samtlige af dem, og jeg kan såmænd tilføje en, som virkelig plager mig, og som man ser hos rigtig mange amatørfotografer inklusive mig selv.  Vi taler om skæv horisont. 

Mange gange er jeg vendt hjem efter en tur med kameraet fuld af forventning til, at jeg denne har fået netop dét ene supershot i kassen, som skal gøre mig rig og berømt.  Men skuffelsen er stor, når jeg endnu engang kan se, at horisonten  gud-hjælpe-mig er skæv igen.

Aaaaaaaargh!

Hver gang jeg er ude med kameraet, tænker jeg: Husk nu at holde det lige, og kig efter guidelines i søgeren.  To minutter efter, har jeg glemt alt om det og kommer hjem med skæve billeder.  En skæv horisont på et landskabsbillede er ikke i orden.

Selvfølgelig kan det rettes op i billedbehandlingen, men så mister jeg pixels, og hvis der er et motiv, som fylder hele billedet, kan det måske ikke lade sig gøre.

Case in point:

DSC_0343

Kigger man i eksempler på nettet på brug af Task, ser man ofte Thread.Sleep anvendt inden i den pågældende Tasks action:

Task.Factory.StartNew(() =>
{
    // Lad være med det her
    Thread.Sleep(1000);
});

Det er sjældent, man bruger Thread.Sleep i andet end eksempelkode, men skulle det ske, må man ikke gøre det sammen med en Task som ovenfor.  Der er ikke garanteret nogen sammenhæng mellem én tråd og en Task.  Task kan skifte mellem tråde – ja, i nogle tilfælde vil en Task endda eksekvere på samme tråd som kalderen, alt efter hvordan man bruger Task.Factory.StartNew og Task.Run.

Hvis man rent faktisk ønsker at stoppe en Task i en tidsperiode, skal man i stedet for Thread.Sleep bruge Task.Delay.

Når man sidder med T-SQL, er det ofte nyttigt at se nogle enkelte rækker for de tabeller, man arbejder med.  Bare for at se hvad man har med at gøre.  I SQL Server Management Studio er det muligt at lave en genvejstast til at vælge de første X rækker fra en tabel.

  1. I Management Studio vælg menuen Tools –> Options. 
  2. Gå til Keyboard –> Query Shortcuts. 
  3. Vælg en tastaturgenvej (f.eks. Ctrl+3) og skriv “SELECT TOP 10 * FROM” (skriv ikke et tabelnavn).
  4. Tryk “Ok”.

image

Start nu en ny query og opret en forbindelse til en database.  Skriv et tabelnavn (og ikke andet), markér det og tryk Ctrl+3 (hvis altså det var Ctrl+3, du valgte ovenfor).  Voila!

image

 

Det spændende ved programmering er, at man hele tiden lærer noget nyt.  Jeg vil forsøge næsten dagligt at skrive på dotninjas.dk, hvad jeg har lært af små ting fornylig.