Ved udvikling af en Windows service i .NET, skal man installere og afinstallere sin service vha. InstallUtil.exe.  I løbet af et udivklingsprojekt kan det blive til rigtig mange forsøg med at installere, starte, stoppe og afinstallere sin service. 

Desværre sker det nogle gange, at InstallUtil.exe ikke kan fjerne servicen, og servicen ender i en tilstand, hvor den hverken kan startes, stoppes eller fjernes.  Eneste mulighed har for mig indtil nu at være en genstart af maskinen – ikke specielt godt for arbejdsglæden.

Jeg har fundet ud af, at taskkill kan løse problemet.  Har man en service, der ikke vil fjernes med InstallUtil.exe, kan servicen slås ned med taskkill, hvis man kender PID for processen.  PID kan findes ved hjælp af “sc queryex”.  Det ser således ud:

 

image

Jeg har efterhånden arbejdet med Microsoft SQL Server i mange år.  Da jeg første gang stiftede bekendtskab med SQL Server, hed det Microsoft SQL Server 6.5.  Siden er der kommet mindst versionerne 7, 2000, 2005, 2008 (R2), Azure og 2012 til.

Til trods for det, vil jeg på ingen måde kalde mig ekspert i hverken sproget T-SQL eller i administration af SQL Server.  Det er mere et nødvendigt onde end en lidenskab.  Jeg foretrækker dog langt mere at skrive rå SQL end en ORM, men det kan måske være et emne til et andet indlæg.

Dermed sker det heller ikke sjældent, at jeg opdager nye ting i T-SQL.  Som f.eks. forleden, da jeg faldt over keywords SOME, ANY og ALL.  Tilsyneladende blev de tre operators introduceret med SQL Server 2005, så de kan næppe kaldes nye.  Men de er altså gået min næse forbi til trods for dens størrelse.

Det sjove ved SOME og ANY er, at de fungerer på samme måde.  SOME lader til at være Microsoft’s opfindelse, mens ANY er standarden.  På dansk svarer “some” til ordet “nogle”, mens “ANY” svarer til “nogen”.  Ordet “any” (og “nogen”) bruges i negeringer og i spørgsmål, så måske har Microsoft tænkt, at man skal bruge ANY i kombination med NOT (altså en negering):

SELECT ...
FROM Tabel
WHERE Kolonne > SOME(SELECT ...)

SELECT ...
FROM Tabel
WHERE NOT Kolonne > ANY(SELECT ...)

Jeg har i dag valgt at lancere det nye design af styrdindiabetes.dk.  Tag godt i mod det.

Godt nytår til jer alle.

Det er lang tid siden forrige indlæg, men der har været travlt med mange andre ting på arbejde og i fritiden.  Det er på tide at komme tilbage “on-track” med styrdindiabetes.dk.

Da jeg fornylig efter lang tids pause genoptog udvikling af styrdindiabetes.dk, virkede login med Facebook/Google/Microsoft pludselig ikke længere i Internet Explorer.  Årsagen er, at jeg i mellemtiden har opgraderet til fra Windows 8 til 8.1 med det resultat, IE er blevet opgraderet til version 11.

En spørgsmål på Mobile Services fora gav et svar fra en hjælpsom MSFT medarbejder: Jeg skulle opgradere Mobile Services JavaScript client library til nyeste version.

Mit forrige indlæg handlede om authorization via Windows Azure Mobile Services.  Authorization består af to elementer:

  1. Kun brugere, der er logget på, må kunne tilgå data.
  2. En bruger må kun kunne tilgå sit eget data.

Sidste gang diskuterede vi punkt 1 – denne gang bliver det punkt 2.

Udfordringen består i at forhindre, at en bruger læser eller redigerer i andre brugeres blodsukkermålinger.  Der er et hav af eksempler på, hvordan man sikrer, at en bruger ikke kan læse andre end sine egne data.  Det kræver blot følgende “where” clause i server-side node.js script for “Read”:

function read(query, user, request) {
    query.where({ userId: user.userId });
    request.execute();
}

Det kniber lidt mere med gode eksempler på, hvordan man f.eks. implementerer “Update” eller “Delete”.  For “Update” valgte jeg at benytte mig af det indbyggede MSSQL objekt, der kan bruges til at eksekvere rå SQL.  Som vi har talt om før, er data backend på Mobile Services blot en SQL Server:

   1:  function update(item, user, request) {
   2:      mssql.query('SELECT TOP 1 Id FROM Entries WHERE Id = ? AND userId = ?', [item.id, user.userId], {
   3:          success: function (results) {
   4:              if (results.length > 0) {
   5:                  item.userId = user.userId;
   6:                  request.execute();
   7:              }
   8:          }});
   9:  }

Idéen er, at jeg først checker, om der findes en entry med det pågældende id for den pågældende bruger.  Er det tilfældet, eksekverer jeg request’en. 

Linje 5 skal sikre, at brugeren ikke forsøger at putte en anden brugers ID på en blodsukkerregistrering.

“Delete” funktionen implementeres tilsvarende.

Vi er dermed klar til at slippe brugerne løs.  De kan logge ind med Facebook, Google eller en Microsoft konto, og de kan oprette blodsukkermålinger på livet løs uden at genere andre.

Authorization er den proces, der foregår, når man styre brugernes adgang til data.  På styrdindiabetes.dk skal det foregå på to niveauer:

  1. Kun brugere, der er logget på, må kunne tilgå data.
  2. En bruger må kun kunne tilgå sit eget data.

Det er punkt 1, vi skal se på i dette indlæg.

Vi har tidligere set på, hvordan Azure Mobile Services har authentication med Facebook o.a. indbygget.  Azure Mobile Services har også indbygget funktionalitet til at styre, hvem der må tilgå de databasetabeller, man opretter i backenden.  Det hele foregår i Azure Management portalen.

For hver tabel oprettet i Mobile Services, findes en Permissions side, hvor man kan angive, hvem der må udføre hver af de 4 CRUD operationer.  Man har følgende fire muligheder:

  • Everyone
  • Anybody with the Application Key
  • Only Authenticated Users
  • Only Scripts and Admins

image

Bemærk at for et website er der i praksis ingen forskel på “Everyone” og “Anybody with the Application Key”, da vores application key står frit tilgængelig i JavaScript’en, så enhver, der kender til højreklik og “Vis kilde” i browseren, kan få fat i nøglen.

Når en bruger via sitet forsøger at tilgå denne tabel vha. client.getTable("Entries"), vil Azure forhindre al tilgang for brugere, der ikke er logget på vha. Facebook eller andet.

I næste indlæg skal vi se på, hvordan vi sikrer, at brugerne kun kan tilgå deres eget data.

Som nævnt bruger Azure Mobile Services en SQL Server som backend database.  Det er ikke en normal SQL Server.  F.eks. findes begrebet foreign keys ikke, og Azure management konsollen giver ikke mulighed for at tilføje kolonner til tabeller som sådan.  I stedet bliver kolonner oprettet dynamisk ud fra det data, man sender til servicen.  Dette er en setting under “Configure” fanen for ens Mobile Service.

image

Husk at slå dynamic schema fra i produktion!

I første omgang har styrdindiabetes.dk behov for en enkelt tabel til at holde de indtastede blodsukkermåling.  En måling består bl.a. af en dato, typen (morgenmad, frokost, aftensmad eller sengetid) og blodsukker før og efter måltidet, hvor det giver mening.  Som JavaScript objekt kunne det se således ud i styrdindiabetes.dk (med lidt knockout.js blandet ind i det):

    var entry =
        {
            type: self.type(),
            beskrivelse: self.beskrivelse(),
            blodsukkerfoer: self.blodsukkerfoer(),
            blodsukkerefter: self.blodsukkerefter(),
            dato: dato,
            antalInsulinEnheder: self.antalInsulinEnheder(),
        };

CRUD operationer foregår vha. en REST API med POST, GET, PUT og DELETE.  Microsoft har heldigvis pakket REST API’en ind i en dejlig JavaScript API.  En CREATE/INSERT/POST operation kan f.eks. se således ud:

    client.getTable("Entries")
        .insert(entry)
        .done(function (result) {
            self.id(result.id);
            // ...
            }
        }, function (err) {
            self.root.errorMessage(err);
        });

JavaScript API’en opbygger HTTP kaldet for os.

Første gang vi laver ovenstående kald til “insert”, vil Mobile Service benytte dynamic schema og tilføje kolonner i Entries tabellen svarende til de properties, jeg definerede på entry objektet ovenfor.

Om  man kan lide dynamic schema er en smagssag.  Man kan godt frygte for integriteten i ens SQL Server database, og at der bliver givet køb på normalisering.  På den anden side øges produktiviteten betragteligt, fordi man kan koncentrere sig om at lave sin applikation og ikke om at designe database og lave datalag etc.  Jeg forestiller mig ikke, at Azure Mobile Services er beregnet til store datatunge projekter.

I næste indlæg skal vi se på, hvordan vi kan bruge login information til at styre adgang til data.

Har man et web site, der kører under https, vil requests til http typisk fejle.  Derfor er det god service overfor besøgende at omdirigere http requests til https.

Jeg faldt over nedenstående til at sætte det op på Azure Cloud Services.  Man tilføjer det til web.release.config, så reglen kun bliver brugt for release build – dvs. typisk ifm. publish til Azure i produktionsmiljø.

<configuration>
  <!-- ... -->
  <system.webServer>
    <rewrite xdt:Transform="Insert">
      <rules>
        <rule name="RedirectToHTTPS" stopProcessing="true">
          <match url="(.*)" />
          <conditions>
            <add input="{HTTPS}" pattern="off" ignoreCase="true" />
          </conditions>
          <action type="Redirect" url="https://{SERVER_NAME}/{R:1}" redirectType="Permanent" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

Som nævnt i forrige indlæg, får man authentication med Facebook, Twitter, Google og Microsoft med i Azure Mobile Services client API’en.  Det hele foregår i gennem det client objekt, vi fik fat i ved at inkludere Azure client JavaScript API’en. 

På min knockout.js viewmodel har jeg kode, der meget ligner følgende:

    self.login = function (providerName) {
        self.errorMessage('');
        client.login(providerName).then(self.afterLogin, function (error) {
            self.errorMessage(error);
        });
    } 

I HTML’en kalder jeg login funktion (gennem knockout.js bindings), hvor jeg angiver providernavnet (fremhævet):

    <ul class="dropdown-menu">
        <li><a data-bind="click: login.bind($data, 'facebook')" href="#">Facebook</a></li>
        <li><a data-bind="click: login.bind($data, 'microsoftaccount')" href="#">Microsoft</a></li>
        <li><a data-bind="click: login.bind($data, 'google')" href="#">Google</a></li>
    </ul>

Det vigtige her er kaldet til client.login(providerName). Det geniale er, at samme kode bruges til alle fire authentication providers: Facebook, Twitter, Google og Microsoft.  Jeg skal bare angive providername, som kan være “facebook”, “google”, “twitter” eller “microsoftaccount”. 

Bemærk at authentication kræver, at man først reigstrerer sig hos de fire udbydere, men det er ret nemt, og Microsoft har lavet en beskrivelse af processen.

Desværre virker ovenstående login metode ikke på Windows Phone.  Det er uheldigt, da et af målene netop var at gøre styrdindiabetes.dk tilgængeligt på mobile platforme.  Problemet er, at IE på Windows Phone ikke understøtter popups, hvilket bruges i login processen.  Jeg har indtil videre valgt at Windows Phone må sejle sin egen sø.  Hvis tiden tillader det, kan der eventuelt komme en Windows Phone app til styrdindiabetes.dk.  Der er muligvis også et workaround på StackOverflow, men jeg har ikke prøvet det, og det løser kun problemet for Facebook.  I så fald skulle jeg lave tilsvarende workarounds for de andre udbydere.  Det er jeg ikke interesseret i at bruge tid på, og hele øvelsen med at bruge Azure Mobile Services skulle netop være at give høj produktivitet uden alt for meget fnidder.

Eftersom jeg har bestemt, at styrdindiabetes.dk skal være en Single Page Application, har jeg brug for en backend, hvor det er nemt at implementere en API, der er nemt at kalde fra JavaScript.  Her er REST med JSON et naturligt valg.

I ASP.NET MVC 4 er det nemt at opbygge en HTTP REST-ish API.  Formålet med relanceringen af styrdindiabetes.dk er dog ikke at opbygge en backend med en relationel database og en API ovenpå.  Det har jeg gjort før, og jeg keder mig allerede bare ved at skrive om det. 

Nej, formålet er at lære noget nyt, og det gjorde jeg heldigvis, da jeg i juni var til TechEd i Madrid.  Her så jeg en (ret poppet) session, hvor Steven Sanderson på 75 minutter bankede en SPA sammen med online booking af taxaer med kort og live pushbacks.  Det gjorde han vha. Windows Azure Mobile Services.

Azure Mobile Services har nogle interessante features.  Server-delen er baseret på node.js.  Databasen er SQL Server, men det er ikke muligt at lave relationer imellem tabeller.  Det kan føles som lidt en skuffelse, men på den anden side er Azure Mobile Services ikke bygget til store databasetunge projekter.  Tabellerne i databasen er dynamiske.  Det betyder, at kolonner dannes automatisk ud fra properties på de objekter, man sender fra klienten (det skal slås fra i produktion!).

Azure Mobile Servies har client API’er til Windows Phone, Android, iOS og ikke mindst til JavaScript.  API’en inkluderer authentication via FaceBook, Twitter, Google og Microsoft, hvilket skal bruges til styrdindiabetes.dk.

Platformen indeholder også andre ting som udsending af mails, push notifications og scheduled tasks.  Det er ikke noget, jeg regner med at få brug for, men man skal aldrig sige aldrig.

Priserne findes på Azure.  Som det fremgår får man 10 tjenester gratis med 20 MB SQL Server.  Det er nok til at lige at komme i gang.

Kode

Efter man har lavet sig en Azure Mobile Service, får man et client application id, som man skal bruge i sin JavaScript:

    <script src="https://styrdindiabetes.azure-mobile.net/client/MobileServices.Web-1.0.0.min.js"></script>

    <script>
        var client = new WindowsAzure.MobileServiceClient(
            "https://styrdindiabetes.azure-mobile.net/",
            "<Indsæt application id her"
        );
    </script>

Igennem client objektet har man nu adgang til hele API’en fra sin Azure Mobile Service, og så er det bare at gå i gang.

For at markere at vi nærmer os 4-årsdagen for diagnosticeringen af min type 1 diabetes, har jeg tænkt mig at relancere www.styrdindiabetes.dk.  Jeg startede det for 3½ år siden, men jeg blev aldrig rigtig selv tilfreds med hverken design eller funktionalitet, og et make-over er hårdt tiltrængt.

Baggrund

En type 1 diabetiker producerer ikke selv insulin.  Mange tror fejlagtigt, at insulin neutraliserer sukker, men insulin hjælper kulhydrater med at blive optaget i kroppens celler.  Når der ikke er noget insulin, kan en diabetiker ikke optage sukkerstoffer fra blodet, og blodsukkeret stiger.  Derfor skal diabetikere indsprøjte insulin i huden, for at hjælpe kroppen til at optage kulhydraterne. 

Indtager man for meget insulin ift., hvad man spiser af kulhydrater, får man for lavt blodsukker (hypoglykæmi), hvilket i løbet af få minutter kan forårsage ildebefindende (insulinchok), og - hvis det ikke behandles hurtigt - bevidstløshed og død.

På den anden side kan for lidt insulin give for højt blodsukker (hyperglykæmi).  Har man for højt blodsukker over en længere periode, kan det give blindhed, fodsår som ikke kan hele (hvilket ofte ender i amputation), manglende følsomhed i fødderne, lever- og nyreskader, hjertekarsygdomme osv.  Listen er lang.  På kort sigt kan hyperglykæmi resultere i ketoacidose, som er en akut livstruende situation, hvor kroppens celler begynder at danne syre pga. manglende sukker.

Dagbog

Som det fremgår, er det yderst vigtigt for en diabetiker at have fuldstændig kontrol over blodsukkeret.  Det gør man ved flere gange dagligt at måle sit blodsukker.  Det gøres med specielle (og pænt dyre) apparater, som kan måle på blod presset ud gennem et hul, man prikker i sin finger eller i sit øre.  Personligt er jeg meget perfektionistisk mht. blodsukkermålinger, og jeg måler gerne op til 10 gange dagligt (det går naturligvis hårdt ud over mine fingre).

Behovet for insulin er indiviuelt og veksler meget fra person til person.  Desuden er der mange faktorer, der spiller ind: Kost, motion, stress, betændelsestilstand i kroppen, smerter (som f.eks. hovedpine), feber, søvn osv.  For at få en forståelse af ens eget insulinbehov, anbefales alle diabetiker i perioder af f.eks. en måneds længde at føre dagbog over blodsukkermålingerne.

Styrdindiabetes.dk

Det er her styrdindiabetes.dk kommer ind.  Styrdindiabetes.dk er en online dagbog for blodsukkermålinger.  Normalt fører man dagbog i en bog, men jeg synes, det er for besværligt, og der er sjældent plads til at gøre notater om motion og andre påvirkninger.  Derfor skal styrdindiabetes.dk være let at bruge og være tilgængeligt på alle platforme (mobil, tablet og desktop).

Min drøm er, at blodsukkerapparaterne bliver udstyret med et SIM-kort, så data automatisk overføres til en web service.  Men producenterne befinder sig stadig i forrige århundrede hvad angår design på apparaterne, så indtil da skal den manuelle registrering være så nem som muligt.

Mål

Jeg har følgende mål med relaceringen af styrdindiabetes.dk:

  1. Nyt og forbedret website hvad angår design og funktionalitet.
  2. Tilegnelse af kompetencer indenfor det nye “hotte” som Single Page Applications (SPAs) vha. Windows Azure Mobile Service, knockout.js, jQuery og Twitter Bootstrap.

Jeg betegner mig selv som nybegynder ud i kunsten af lave SPAs, så det bliver en spændende proces.  Jeg vil forsøge at beskrive, hvad jeg måtte møde af udfordringer her på dotninjas.dk.