Injecting ILogger like all the cool ASP.NET Core kids

Cross posted from https://dev.givetheiron.se/2019/10/26/injecting-ilogger-like-all-the-cool-asp-net-core-kids/

Just about every service class used in our ASP.NET Classic sites need a logger. The Nlog way to do that is to put this in your class

private static readonly NLog.Logger Logger =
    NLog.LogManager.GetCurrentClassLogger();

But after setting up logging in ASP.NET Core I really wanted some ILogger Dependency Injecting magic. With Dependency Injecting being a fundamental part of ASP.NET Core, setting up an ILogger is done by Injecting the logger into the constructor

public class WorkingClass
{
    private readonly ILogger _logger;
    public WorkingClass(ILogger<WorkingClass> logger)
    {
        _logger = logger;
    }

Injecting the logger not only makes the code cleaner, it’s also far easier to test. So how do I get me a piece of that action?

StructureMap to the rescue

Note that we use StructureMap as our Dependency Injection Framework, but I’m sure the following has similar ways in your favorite Dependency Injecting framework. Anyhoo, to setup the Injection we use the StructureMap Registry

public class DomainRegistry : Registry
{
    public DomainRegistry()
    {
        Scan(
            scan => {
                scan.TheCallingAssembly();
                scan.WithDefaultConventions();
            });
				
        For<ILogger>().AlwaysUnique().Use(c => GetLogger(c));
    }
    private ILogger GetLogger(IContext context)
    {
        return NLog.LogManager.GetLogger(
            context.ParentType?.FullName ?? "Datema.EasyShop");
    }
}

So lets break it down. First line of interest

For<ILogger>().AlwaysUnique().Use(c => GetLogger(c));

Since the Logger is unique per class AlwaysUnique() is added after the For<ILogger>(), which will make sure the call to Use(…) gets called for each object that want’s a logger. The last part is Use(c => GetLogger(c)); which is required since it’s not allowed to have a null propagating operator in a lambda expression. Why use one of those? Lets find out!

return NLog.LogManager.GetLogger(
    context.ParentType?.FullName ?? "Datema.EasyShop");

The interesting bit here is what’s passed into the GetLogger call. The StructureMap IContext we get from the Use(c => GetLogger(c)); call knows which type it’s creating the ILogger for, and it’s type is stored in context.ParentType. That means we can use the ParentType.FullName to create a new Logger with the full name of the class being created. That’s exactly what we want to achieve, so why the whole ”?.” or null propagating operator? The DomainRegistry will eventually be inserted into a StructureMap Container, and on startup of the application, there’s a call to container.AssertConfigurationIsValid(); to make sure it has everything it needs. The call to AssertConfigurationIsValid() works by attempting to create every object the IContainer is tracking. This means it will attempt to create a ILogger directly, without a value set in context.ParentType since there is none. With more complex DI scenarios it’s easy to miss something, and failing on startup is better than later on when some specific part of the system is accessed.

Static fields in generic classes

Cross posted from https://dev.givetheiron.se/2019/06/12/static-fields-in-generic-classes/

In a new attempt to reboot my blogging I’m putting my exploratory coding sessions in writing (although I’m not sure I can call it reboot, since I’m not sure I booted my blogging to begin with). With exploratory coding I mean that when I come across a question I have, instead of immediately searching for the answer, I try and explore it with code instead. I use LINQPad since it’s so fast to get up and running. So over to the question.

Question: Static fields are shared in all instances of a class, but what happens if the class is a generic class?

The specific problem I had was I’m storing a token in a static string, but I didn’t want the token to be shared between different concrete types. So, in the following code, would the StaticString be shared between GenericClass<int> and GenericClass<string>?

void Main()
{
    GenericClass<int>.StaticString = "genericInt";
    GenericClass<string>.StaticString = "genericString";
    GenericClass<int>.StaticString.Dump();
    GenericClass<string>.StaticString.Dump();
}

public class GenericClass<T>
{
    public static string StaticString { get; set; }
}

The result is good for me for the problem I have

I'm a GenericClass<int> StaticString
I'm a GenericClass<string> StaticString

As you can see by the result, they aren’t shared. Now I remember from way back I read that Generics in the Run Time work differently with value types and reference types (https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/generics-in-the-run-time) so to be sure I test with another reference type as well.

    ...
    GenericClass<Exception>.StaticString = "I'm a GenericClass<Exception> StaticString";
    GenericClass<Exception>.StaticString.Dump();

And the result is more of the same

I'm a GenericClass<int> StaticString
I'm a GenericClass<string> StaticString
I'm a GenericClass<Exception> StaticString

I could have added unit tests to verify this, but it seems unnecessary to have unit tests covering the C# language. I just needed to check how the language behaves for generics.

Läs och skriv till MongoDB med C# driver

I vårt projekt med Fältrapportering med TC55 så har jag nått fram till att läsa våra undersökningar från, och skriva ner våra rapporter till, MongoDB. Det är detta som kallas CRUD, vilket är en förkortning för Create, Read, Update, Delete, det vill säga Skapa, Läsa, Uppdatera, Ta bort. Eftersom jag är en C#-utvecklare så använder jag MongoDB C# drivers.

Installera MongoDB C# driver

Ända sedan Visual Studio 2010 så ska din första instinkt för att hämta kodbibliotek vara att kolla NuGet. Det gäller givetvis i MongoDB C# driver-fallet också. Öppna NuGet och sök efter MongoDB. Bland de översta träffarna (den översta när denna blog skrivs) ska vara Official MongoDB C# driver och det är bara att klicka på installera så är allt redo.

MongoDB är väl indelat i många olika namespace:s så var beredd på du kommer behöva lägga till nya using för att intellisense:n ska fungera för mycket av det du kommer använda. De första intressanta objekten ligger i MongoDB.Driver så en using MongoDB.Driver; löser de omedelbara problemen.

Uppkoppling mot MongoDB

I vårt projekt har jag än så länge inte stött på något behov av att använda något annat än MongoDB standard, så även om uppkopplingen mot MongoDB har många avancerade inställningsmöjligheter så går jag inte in på de här.

Första steget är att skapa en MongoClient, som är objektet som hanterar uppkopplingen mot MongoDB. Den hanterar de samlade uppkopplingarna (Connection Pooling) och övrig uppkopplingslogik.

    var client = new MongoClient("mongdb://test:test@localhost/test");

I exemplet ovan använder jag det sättet att skapa en MongClientden med en URL till databasen. URL:en skrivs på formatet

mongodb://<username>:<password>@<hostname>/<database>

Därefter hämtar vi ut en MongoServer ur MongoClient, vilket skapar en ny uppkoppling med de inställningarna som är registrerade i MongoClient.

    var server = Client.GetServer();

Och till sist behövs en MongoDatabase som hämtas ur MongoServer.

    var database = server.GetDatabase("test");

Nu är vi klara att börja läsa och skriva till MongoDB.

CRUD med MongoCollection

Kort om MongoDB som dokumentsdatabas

MongoDB är en dokumentsdatabas. Som relativ nybörjare i området så är jag inte säker på allt vad det betyder, men i grunden så dokumentsdatabaser anpassade för att lagra objekt som är grupperade på något specifikt sätt. Det kan vara ett faktiskt dokument som lagras grupperade med Titel, innehåll, kommentarer, författare och så vidare, där allt lagras i en djup struktur med klasser och underklasser. I C# världen skulle den kunna se ut såhär

public class Memo
{
    public string Title { get; set; }
    public string Body { get; set; }
    public Author Author { get; set; }
    public List<Comment> Comments { get; set; }
}

När ett dokuement som ovan lagras i MongoDB, så kommer MongoDB automatiskt skapa upp ett _id i databasen med ett automatiskt genererat 12-bytes GUID. Det är bara rotobjektet som får detta GUID, det vill säga att Memo-dokumentet kommer få ett _id men inte Author eller Comments. För att nå id:t i din klass så är det enklast att använda sig av datatypen ObjectId. I det fallet klarar C# drivers av att serialisera id-fältet åt dig

public class Memo
{
    public ObjectId Id { get; set; }
    ...
}

Namnet kan vara Id, id eller _id och serialiseringen kommer att fungera utan problem.

Dags att läsa och skriva

Att läsa och skriva till MongoDB sker via en MongoCollection. En Collection i MongoDB är en samling dokument och motsvarar löst en tabell i en relationsdatabas. Om du skapar en collection med den generiska funktionen så får du en MongoCollection som klarar av automagisk översättning av dina klasser till BSON som MongoDB förstår (BSON är en binär representation av ditt data i ett format liknar JSON). Det innebär att om du använder den generiska versionen av MongoCollection så är det bara att skicka in dina objekt och låta MongoDB hantera serialisering och deserialisering till MongoDB. För att få tag på en MongoCollection så skapar du den via MongoDatabase.

    public class Author
    {
        public ObjectId Id { get; set; } 
        public string Name { get; set; }
    }

    // Stoppa in namnet på din Collection i GetCollection
    var collection = database.GetCollection<Author>("Author");

För CRUD-operationerna (samt möjligheten att hämta alla objekt) så gör du följande:

    // Insert (Create)
    public void Insert(Author author)
    {
        collection.Insert(author)
    }

    // Get (Read)
    public void Get(ObjectId Id)
    {
        return collection.FindOneById(Id);
    }

    // Update
    public void Update(Author author)
    {
        collection.Save(author);
    }

    // Delete
    public void Delete(Author author)
    {
        var query = Query.EQ("_id", author.Id);
        collection.Remove(query);
    }

    // Get all
    public void GetAll()
    {
        collection.FindAll();
    }

Delete använder sig av en MongoQuery. Om våra behov blir mer avancerade framöver så kanske jag går in på hur den fungerar.

I nästa del så beskriver jag hur jag byggde upp klasstrukturen kring CRUD-operationerna och uppkopplingen till MongoDB.

Installera MongoDB på Windows Server

Följande är en beskrivning av hur jag installerade MongoDB på Windows Server 2012, men proceduren ska vara i stort sett likadan för alla Windows Vista/Server 2008 SR2 eller senare.

Med installation av MongoDB så är det nedan punkter jag kommer gå igenom.

Fortsätt läsa

Fältrapportering med TC55 (Android), ASP.NET och MongoDB

En riktigt härlig del med att vara utvecklare på Datema är att vi har både stora till små projekt. Vi har bland annat PSS – Självskanning som är ett långlivat projekt med konstant utveckling. På andra sidan står de små ofta kundspecifika projekten, som visserligen kan leva under lång tid, men som vanligtvis innebär en intensiv period av utveckling, följt av en lång period av support med endast sporadisk utveckling när omvärlden kräver det. Vi har också projekt som landar någonstans i mitten, där Easy Sales vår lösning för fältsälj är ett bra exempel. Det är ett system som lever i cykler av kortare perioder av utvecklingen följt av en tid av underhåll.

Easy Field Service

Datema har lång erfarenhet av datainsamling och ett av systemen som snurrat längst är Easy Field Service. Den består av ett Web Service-lager ovanpå en MSSQL-databas, och en applikation skriven för bättre begagnade Windows Mobile 6.5 (vilket inte ska blandas ihop med Windows Phone som är Microsofts nuvarande operativsystem för mobiltelefoner). Den vanligaste handdatorn som används av Easy Field Service idag är ES400 från Motorola Solutions. och den ska nu bytas ut. Vi har ett tätt samarbete med Motorola Solutions, så ersättaren till ES400 blev nästa generation, den Android-baserade TC55:an.

Flört med världen utanför Redmond

Eftersom vi ändå behöver bygga en ny klient och byta ut stora delar av kommunikationen till servern så passar vi på och lyfter hela plattformen till senaste nyaste. Vi jobbar huvudsakligen med C# och Microsofts ASP.NET så valet föll till ASP.NET Web API för ett REST API för appen och ASP.NET MVC för att slunga HTML till en webb-backend. Det som blir lite spännande i detta projekt är att efter moget och demokratiskt övervägande av alla utvecklare inte på semester (dvs. mig själv) så använder vi MongoDB som databas eftersom systemets dynamiska struktur är klart bättre lämpad för en dokumentsdatabas än en relationsdatabas (jag har hanterat trädstrukturer i en relationsdatabas tidigare och det innebär mycket smärta och tårar för att få till på rätt sätt). Utöver databasförändringarna så har närvarande utvecklare även beslutat att AngularJS kommer användas i webbläsaren. Exakt hur SPA-liknande det blir i slutändan återstår att se, men efter att ha upplevt MVVM-magin med KnockoutJS i ett par projekt så lär det ta ett tag innan jag lämnar MVVMs automagiska värld.

Mina erfarenheter kring allt detta kommer jag skriva om i senare inlägg och först upp är installationen av MongoDB.

Del 1: Installera MongoDB på Windows Server är ny tillgänglig
Del 2: Läs och skriv till MongoDB med C# driver

Förenkla LINQ to Entity Framework med Extensions Methods del 2

Detta är en fortsättning på https://dev.datema.se/2014/06/04/forenkla-linq-to-entity-framework-med-extensions-methods-del-1/

Filtrera vid behov

Eftersom LINQs underliggande struktur är ett träd, så är det väldigt enkelt att klistra på metoder där de behövs. I vår kod så har jag använt detta för att lägga på filter vid behov. OBS! Följande sätt att peta in Where lite här och där i LINQ-uttrycken tenderar att skapa horribla SQL-frågor. Om du jobbar med stora datamängder så är det mycket möjligt att detta kommer orsaka frågor som tar lång tid.

public class CoreBase
{
    public int CustomerId { get; set; }
    protected internal IQueryable _customers
    {
        get 
        {
            if (CustomerId > 0)
                return _context.Customers.Where(x => x.Id == CustomerId);
            return _context.Customers;
        }
    } 

    public int AdministratorId { get; set; }
    protected internal IQueryable _people
    {
        get
        {
            if (AdministratorId > 0)
                return _context.People.Where(x => x.Id == AdministratorId);
            return _context.People;
        }
    }
}

Jag har exponerat flertalet av mina DbSet på detta sätt. När jag exponerar metoder till övre lager för att exempelvis läsa ordrar så är det väldigt enkelt att lägga på filter för att endast läsa alla ordrar för en viss kund.

    core.CustomerId = 3; 
    var orders = core.GetOrders();
    
    // och i coreklassens basklass 
    public List GetOrders() 
    { 
        return (from o in _orders
                join c in _customers on o.CustomerId equals c.Id
                select o).ToList();
    }

Ogres and code is like onions, it has got layers

Vi använder Entity Framework Code First i projektet, så vi låter Entity Framework generera databasen åt oss. Mellan Entity Framework och databasen finns en ADO-adaptern som sköter översättningen av LINQ-uttrycken till SQL. ADO-Adaptern för SQLite stödjer inte sådant som att skapa databaser och ändra tabeller. Vi löste det problemet med arv, där vår basklass har definitionen av våra DbSet och diverse stödfunktioner, och all databasspecifik kod ligger i de ärvda klasserna.

    public abstract class ContextBase : DbContext
    {
        public DbSet<Customer> Customers { get; set; }
        ...
    }

    public class SqlContext : ContextBase
    {
        ...
        static SqlContext()
        {
            Database.SetInitializer<RegistryContext>(new CreateInitializer());
        }

        class CreateInitializer : CreateDatabaseIfNotExists<RegistryContext>
        {
            ...
        }
    } 

    public class SQLiteContext : ContextBase
    {
        ... 
        static SqlContext()
        {
            Database.SetInitializer<ClientRegistryContext>(new CreateSqlLiteInitializer());
        }

        public class CreateSqlLiteInitializer : IDatabaseInitializer<ClientRegistryContext>
        {
            public void InitializeDatabase(ClientRegistryContext context)
            {
                // I SQLite fallet så skapar vi själva upp tabellerna med SQL
                // via SqlQuery på context.Database
                context.Database.SqlQuery("CREATE TABLE...");
                ...
            }
        }
    }

Det ger mig fördelen att jag kan enkelt kontrollera om en viss Context används av SQL eller SQLite.

Entity Framework med SQLite

Att använda Entity Framework med MS-SQL och SQLite går generellt sett bra, men SQLite har problem med vissa mer komplexa LINQ-uttryck. Två punkter va viktiga för mig att tänka på för att hitta en lämlig lösning. Det ena är att LINQ mot databaser är i stort sett en delmängd av funktionaliteten av det som kan användas i LINQ to Objects. Med andra ord så borde alla LINQ-uttryck som resulterar i databasanrop, ge exakt samma resultat om de istället jobbar mot t ex listor. Den andra punkten är att i detta projekt så innehåller SQLite databaserna endast en liten den av den stora databasen, och vi har inte något fall en där datamängderna är större än några tusen rader. Med de kunskaperna i åtanke så beslutade jag att ta kostnaden att läsa allt data i minnet och använda LINQ to Objects istället. Lösningen blev att i min basklass som jag redan skapat för dessa mer komplicerade frågor så la jag till en till Extension Method för att läsa upp data i minnet om det inte är ett anrop mot MS-SQL. Det sker enkelt via en .ToList(), och en .AsQueryable() för att exponera listan som en IQueryable.


    public static class CombinedCoreExtension
    {
        public static IQueryable<T> Transform<T>(this IRegistryContext context, IQueryable<T> source)
        {
            // Om frågan är mot en MS-SQL-databas så behövs ingen förändring
            if (context is RegistryContext)
                return source;

            // Om det är mot SQLite så läs upp i minnet.
            return source.ToList().AsQueryable();
        }
    }

Sedan ändrade jag min Core-bassklass så den använder denna nya Extension Method.

    protected internal IQueryable _customers
    {
        get 
        {
            // Här lades Transform till
            if (CustomerId > 0)
                return _context.Transform(context.Customers).Where(x => x.Id == CustomerId);
            return _context.Transform(context.Customers);
        }
    } 

Tester är bra, inte bara för testning

Om du har läst del 1 så kanske du undrar över de Extension Methods som jag skapade där. De borde orsaka NullReferenceException när en List försöks cast:as till DbSet. Mina exempel va förenklingar för att få de lite kortare. Metoderna ser egentligen ut på sätt:

    public static class CustomerExtensions
    {
        public static IQueryable EagerlyLoadOrdersRowsAndPeople(this IQueryable source)
        {
            if (s is DbSet)
                return (source as DbSet).Include(x => x.Orders.Select(x => x.Rows)).Include(x => x.People);
            return source;
        }
    }
 

Vi gör på det sättet eftersom i våra enhetstester så använder vi inte Entity Framework utan använder minnesstrukturer.

Förenkla LINQ to Entity Framework med Extensions Methods del 1

Den stora fördelen med Entity Framework är LINQ to Entities. Det är extremt enkelt att modulärt bygga på IQueryable vid behov. Till exempel så är det inte ovanligt att vilja läsa in refererade entities direkt (Eagerly Load) som normalt laddas in vid behov (Lazy Load). För att göra detta i Entity Framework så används Include-funktionen

    Context.Customers.Include(x => x.Orders)

I detta enkla scenario är det inte svårt att förstå vad som ska hända, men i ett mer krångligt scenario så blir koden mindre uppenbar

    // För att automatiskt läsa in ett länkat objekts 
    // egna länkade object så använd Select inne i Include
    Context.Customers.Include(x => x.Orders.Select(x => x.Rows)).Include(x => x.People)

Vad jag då hellre ser är en förklarade text för vad jag vill göra.

    Context.Customers.EagerlyLoadOrdersRowsAndPeople()

Detta är busenkelt med en eller flera Extension Methods.

public static class CustomerExtensions
{
    public static IQueryable<Customer> EagerlyLoadOrdersRowsAndPeople(this IQueryable<Customer> s)
    {
        var customers = (DbSet<Customer>)s;
        return customers.Include(x => x.Orders.Select(x => x.Rows)).Include(x => x.People);
    }
}

Det går även bra attt bygga upp dessa av varandra

public static class CustomerExtensions
{
    public static IQueryable<Customer> EagarlyLoadOrdersRowsAndPeople(this IQueryable<Customer> s)
    {
        return s.EagarlyLoadOrdersAndRows().EagarlyLoadPeople();
    }
    
    public static IQueryable<Customer> EagarlyLoadOrdersAndRows(this IQueryable<Customer> s)
    {
        var customers = (DbSet<Customer>)s;
        return customers.Include(x => x.Orders.Select(x => x.Rows));
    }
    
    public static IQueryable<Customer> EagarlyLoadPeople(this IQueryable<Customer> s)
    {
        var customers = (DbSet<Customer>)s;
        return customers.Include(x => x.People);
    }
}

TypeScript med Knockout.js – första erfarenheterna

My life with the Cargo Code Cult

När man stöter på något man inte förstår kan man antingen ge upp direkt eller så tar man smällen, visa sig stor som människan, rullar upp ärmarna och gör jobbet för att lära sig. Så när jag stötte på JavaScript så valde jag naturligtvis att ignorera existensen av JavaScript och ta mig an TypeScript istället. I ärlighetens namn har jag bråkat med JavaScript i flera år. Jag tog mig aldrig an något mer avancerat än Cargo Cult-programmering av vymodeller med Knockout.js men med TypeScript så har jag hittat min brygga mellan det dynamiska och prototypbaserade JavaScript och C# som jag framgångsrikt har kodat med i flera år nu. Så det va med en viss iver jag tog mig an att bygga om ett av mina Cargo Cult-uppdrag till TypeScript och Knockout.

It’s a trap!

Den första fällan jag gick i va att direkt slänga all min nya kod i en module. Jag hade läst någonstans att modules är som namespaces och all min C# kod ligger i ett namespace, och därmed ska givetvis min TypeScript-kod ligga i ett namespace:

module administration {
    class ViewModel {
    }
}

Riktigt så enkelt är det inte. Det är antagligen inget fel på modules, men förstår man inte alla konsekvenser av detta (vilket jag uppenbarligen inte gjorde) så är det stor risk att stöta på problem när man rör sig utanför de enklaste exemplen.

DevExpress

Vi använder DevExpress-komponenter i vårt projekt. DevExpress bakar in jQuery i sina komponenter och vanligtvis är detta inte ett problem. När jag hade min kod i en module däremot så blev alla mina anrop till $.map anrop till DevExpress’s inbakade version av jQuery.

Utökning av Knockout

Tillägg med push i en observableArray innebär att DOM:en uppdateras för varje anrop. Jag googlade mig fram till http://jsfiddle.net/johnpapa/nfnbD/ som visar ett enkelt sätt att utöka knockout med en pushAll. John Papas lösning påminde mig om Extension Method-begreppet i C# så jag började genast att försöka översätta det till TypeScript kod. Efter en del försök så kändes följande som det borde fungera:

module administration {
    interface KnockoutObservableArray<T> extends KnockoutObservable<T[]>, KnockoutObservableArrayFunctions<T> {
        pushAll(items: T[]): void;
    }
     
    ko.observableArray.fn['pushAll'] = function (valuesToPush: Array<any>) {
        var underlyingArray = this();
        this.valueWillMutate();
        ko.utils.arrayPushAll(underlyingArray, valuesToPush);
        this.valueHasMutated();
        return this;
    }
}

Nu vet jag att det hade fungerar rätt väl om jag bara hade slopat module. Jag antar att det beror på att en extension måste ligga i samma scope som det som utökas.

Det ska nämnas att interface:et som utökas måste återdefinieras med samma signatur. Följande fungerar inte:

interface KnockoutObservableArray<T> {
    pushAll(items: T[]): void;
}

this != this && this.self === undefined

Det andra stora hindret är att ett vanligt mönster i vymodellerna för Knockout, och säkerligen andra JavaScripts-användningar, är att binda en lokal variabel till this:

var AccessViewModel = function() {
    var self = this;
}

Den är bra att ha eftersom i Knockout går det utmärkt att binda till click och andra events. I event, och som jag förstått det andra callback-anrop som har sitt ursprung utanför JavaScript, så sätts this till objektet som genererade callback-anropet. Exempelvis vid musklickning blir this satt till html-objektet som klickades på.
Problemet med mönstret var self = this; är att i TypeScript så nås alla klassvariabler via this.

class User {
    firstName: string;
    lastName: string;
    constructor(firstName: string, lastName: string) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    showName(): void {
        alert(this.firstName + ' ' +  this.lastName);
    }
}

Om showName är triggat från ett click i Knockout-vyn:

<input type="submit" data-bind='click: $parent.showName' />

så kommer this i showName ovan inte vara objektet som funktionen ligger på, och att fixa en var self = this; funkar inte för this används för att nå klassvariabler:

class User {
    ...
    self: User;
    constructor(firstName: string, lastName: string) {
        ...
        this.self = this;
    }

    showName(): void {
        // self fungerar inte eftersom den också måste nås via this
        alert(this.self.firstName + ' ' +  this.self.lastName);
    }

En lösning runt det problemet är att skapa din funktion som en tom funktionsvariabel, och sätta den i konstruktorn med en arrow function. För C#:are så är en arrow function liknande ett lambda-uttryck. Om en sådan används så ändrar det beteendet på this som istället innehåller referens till klassen i vilken funktionen är definierad:

class User {
    firstName: string;
    lastName: string;
    showName: () => void;

    constructor(firstName: string, lastName: string) {
        this.firstName = firstName;
        this.lastName = lastName;
        showName = () => {
            // this går bra att använda här för att nå klassens variabler
            alert(this.firstName + ' ' +  this.lastName);
        }
    }
}

Lämna gärna en kommentar, eller så hittar ni mig i skuggorna på twitter under @freljung.

Självkonfigurerande applikationer

”Jag hittar själv det grönaste gräset” – S.T. Allion

Som utvecklare koncentrerar man sig på hur applikationen skall fungera i produktion. Ibland glömmer man bort att göra det enkelt att deploja och köra applikationen på sin egen utvecklingsmaskin. När man lägger till ett externt beroende tänker man kanske inte på att kollegorna blir tvungna att göra samma inställningar på sina maskiner som du med möda och besvär gjort på din maskin. Den utvecklare som kanske kommer att få mest problem blir du själv när du försöker sätta upp utvecklingsmiljön på en ny dator.

Ett typiskt externt beroende för en normal webbapplikation är åtkomsten till en databasserver. Om inte databasaccessen är korrekt konfigurerad kommer applikationen att fallera. Den enda ”hjälp” att fixa problemet du får är oftast några hundra raders stacktrace. Felmeddelandena handlar om följdproblem där man själv måste knyta symptomet till rotproblemet.

Men vänta lite, detta är ju ett lösbart problem! Det handlar om att tillämpa en enkel uppsättning regler.

Grundregler för externa beroenden

  1. Kontrollera vid uppstart om allt är på plats. Om inte, gå vidare till nästa punkt.
  2. Kan vi automatiskt lösa problemet genom en enkel sökning? Om inte, gå vidare till nästa punkt.
  3. Mänsklig assistans behövs. Kan vi först reducera antalet potentiella lösningar? I så fall presentera dessa val för användaren. Om det inte är möjligt, gå vidare till nästa punkt.
  4. Informera tydligt vad applikationen behöver, och hur man normalt sett går tillväga för att göra dessa inställningar.

Låt oss tillämpa dessa regler på beroendet av en databas. Här kommer ett förslag i form av javascriptformaterad pseudokod.

Application.connectToDatabase()

Application.connectToDatabase = function()
{
   var db;
   var settingsFile = loadSettings();
   if (settingsFile && settingsFile.dbSettings.correctlyFormatted())
   {
      var dbServer = connectToDbServer(settingsFile.dbSettings);
      if (!dbServer.connected())
      {
         dbServer = UserDialog.helpUserStartUpTheDbServer();
      }
      db = dbServer.getDbForCurrentApplication();
      if (!db.isValid())
      {
         db = UserDialog.helpUserCreateNewDb(dbServer);
         settingsFile.saveDbSettings(db);
      }
   }
   else
   {
       var localDbServer = connectToDbServer(LOCAL_HOST_DB_SETTINGS);
       if (localDbServer.connected())
       {
          db = dbServer.getDbForCurrentApplication();
          if (!db.isValid() || !UserDialog.letUserCorfirmDbChoice(db))
          {
             db = UserDialog.helpUserFindDbServerAndSelectDb();
          }
       }
       else
       {
          db = UserDialog.helpUserFindDbServerAndSelectDb();
       }
       settingsFile.saveDbSettings(db);
   }
   return db;
}

Vänlighet

I koden ovan tillämpar jag grundreglerna rekursivt. Funktionerna i UserDialog hjälper användaren enligt samma regler och principer.

Egentligen är dessa Grundregler för externa beroenden samma som grundreglerna för användbarhet och användarvänlighet. Det handlar bara om att tillämpa dom i lite högre grad än vad som är brukligt. Det finns egentligen inget som säger att databasuppkopplingskoden ovan inte skall vara kvar i produktion. Även vid driftsättning till prod behöver man hjälp ibland. Åtminstone behöver man bra hjälpmeddelanden, inte dåliga felmeddelanden. Var snäll.

JavaScript && Ordning && Reda

Det är enkelt att med lite JavaScript öka användarvänligheten på en i övrigt statisk webbsida. Man stoppar in en funktion här, och lite jQuery där. Men så fort det blir fler än ett par funktioner behöver man lite mer ordning och reda. Nu skall jag beskriva ett enkelt sätt att få en vettig struktur på din JavaScript-kod. Kanske är jag färgad av Java och C# för strukturen liknar mycket en vanlig klassdefinition.

Vi skall göra ett knapp-objekt med dom publika metoderna enable() och disable(). Här är ett par screenshots av objektet in action.

buton_disabled

buton_enabled1

Sidan

Htmlkoden för ovanstående är följande:

<label for="inputField">Please type "enable":</label>
<input id="inputField" type="text">
<a id="button" href="javascript: alert('Button clicked');">
    <img src="button.png">
</a>

Och här kommer javascriptkoden på samma sida:

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js">
</script>
<script src="Button.js"></script>
<script>
    $(document).ready(function ()
    {
        // Här skapar vi vårt button-objekt med en fabriksmetod som
        // finns i filen Button.js.
        var button = DDT.Button($("#button"));

        // Vid varje tangenttryckning anropar vi antingen enable()
        // eller disable() på vårt button-objekt.
        $("#inputField").keyup(function (e)
        {
            if ($("#inputField").val().indexOf("enable") != -1)
                button.enable();
            else
                button.disable();
        });
    });
</script>

Mer än så behövs inte för att använda vårt knappobjekt. All intressant kod ligger i Button.js.

Button.js

// Skapa först ett "namespace"/"package". DDT står för
// Datema Dev Team.
var DDT = DDT || {};

// Detta är fabriksfunktionen som skapar och returnerar
// Button-objekt.
DDT.Button = function(buttonAnchor)
{
    // Privat medlemsvariabel
    var href;

    // Privata "konstanter"
    var SUFFIX_GRAYED_OUT = "_grayed_out.png";
    var SUFFIX_NORMAL = ".png";

    // Detta är vår konstruktor
    var make = function()
    {
        // Först sparar vi undan aktuell href på den anchor-tagg som
        // skickades in från sidan.
        href = buttonAnchor.attr("href");

        // Nu är vi redo att skapa och returnera själva
        // Button-objektet med dess två publika metoder.
        return {
            enable: enable,
            disable: disable
        }
    };

    // Den publika metoden enable
    var enable = function (id)
    {
        if (isEnabled()) return;
        buttonAnchor.attr("href", href);
        var imgSrc = buttonAnchor.children("img").attr("src");
        var imgSrcNormal =
               imgSrc.replace(SUFFIX_GRAYED_OUT, SUFFIX_NORMAL);
        buttonAnchor.children("img").attr("src", imgSrcNormal);
    };

    // Den publika metoden disable
    var disable = function()
    {
        if (!isEnabled()) return;
        buttonAnchor.removeAttr("href");
        var imgSrc = buttonAnchor.children("img").attr("src");
        var imgSrcGrayedOut =
               imgSrc.replace(SUFFIX_NORMAL, SUFFIX_GRAYED_OUT);
        buttonAnchor.children("img").attr("src", imgSrcGrayedOut);
    };

    // En privat metod som används från enable och disable.
    var isEnabled = function()
    {
        return buttonAnchor.attr("href");
    };

    // Här anropar vi konstruktorn och returnerar Button-objektet.
    return make();
};

Ordning och reda

Som synes får man en klasslik struktur på koden. Överst kommer privata medlemsvariabler och konstanter.

Därefter följer konstruktorn som jag döpt till make(). Den ser inte ut att att ta några argument men det gör den! Själva fabriksmetoden DDT.Button() tar argumentet buttonAnchor vilket därmed blir tillgängligt för alla lokala metoder inklusive make().

Funktionen make() skapar och returnerar själva Button-objektet. Det enda som blir synligt utifrån blir metoderna enable() och disable(). Dessa metoder kan vi på ett snyggt sätt implementera efter make() eftersom make() ännu inte körts.

Efter enable() och disable() kommer den privata metoden isEnabled(). Det enda som skiljer den från dom publika metoderna är att den inte returneras som en del av Button-objektet i make().

Till sist måste vi faktiskt göra något annat än att skapa lokala variabler och funktioner. Vi anropar nu make() och returnerar dess resultat.

Slutsats

Det finns många sätt att organisera sin kod i JavaScript. Ovanstående mönster tycker jag är ett enkelt sätt att skapa en lättläst struktur.