Code-first med ASP.NET MVC og EF

I dette innlegget vil jeg gi en liten introduksjon til Code-first-utvikling mot .NET sitt MVC-rammeverk ved hjelp av Entity Framework. Dette er en metodikk .NET-gruppen har benyttet i sommerprosjektet.

Tanken bak

ASP.NET MVC er et web-rammeverk som er strukturert etter MVC-designmønsteret (Model View Controller). En MVC-arkitektur kan tolkes litt ulikt, men i hovedsak kan vi tenke at “Model” er selve objektene vi jobber på, altså datacontainere, Controller-en samler data fra modellene og har logikk for å gjøre data klart for visning, mens View-et presenterer data for brukeren.

Fordelen med å utvikle med et slikt rammeverk i bakhånd ser man fort når man har mye funksjonalitet, ved vedlikehold og særlig når man jobber i team. Konvensjoner og abstraksjon er nøkkelord her. Når man skal vedlikeholde en MVC-applikasjon, kan man lettere finne frem og forstå kodebasen når det følger et mønster, slik som i MVC. Gjør man seg flid og skriver gode tester i alle lag oppover, er det også lett å abstrahere vekk lag i en stor applikasjon. I tillegg til at rammeverket selvsagt gir deg mye gratis kode, gjør alt dette at utvikling er effektivt.

I tillegg til MVC, må man på en måte serialisere data som ligger i modellene. Det er vanlig å derfor ha en database i bakhånd. Når vi skal lage en MVC-applikasjon står vi overfor noen designvalg helt i begynnelsen. Den vanligste måten, hvertfall i Visma, er “Database-first”. Dette vil si at du designer entiteter først, og deretter skriver koden. Modellene her blir stort sett bare brukt som datacontainere, og man ser lite til de.

I vårt sommerprosjekt tok vi utfordringen ved å prøve noe nytt. Ingen av våre ressurspersoner hadde erfaring med Code-first, som er den andre metoden. Tanken bak Code-first er at man beskriver objektene som modeller i kode og ved hjelp av Entity Framework blir databasen generert utifra dette. Databaselaget er derfor helt abstrahert for deg som utvikler (iaf. i teorien). Code-first egner seg nok best for nye prosjekter, da man ofte har en database fra før av i vedlikholdsprosjekter.

Nok prat, la oss se på litt kode.

I praksis

La oss ta for oss et scenario og løse det med Code-first.

Du skal lage et system som holder styr på staller og hester. En stall kan ha flere hester hos seg, men en hest kan bare høre til én stall. Først lager vi modellene.

public class Stable {
  public int Id { get; set; }
  public string Name { get; set; }
  public string Location { get; set; }
  public virtual ICollection<Horse> Horses { get; set; }
}
public class Horse {
  public int Id { get; set; }
  public string Name { get; set; }
  public virtual Stable Stable { get; set; }
}

 
Hold your horses! Hva har vi gjort sålangt?
Vi har definert to objekter: Stable og Horse.
Datafeltene til objektene er definert ved properties.
Relasjonen er definert ved at en stall har en collection av hester, og en hest har en referanse til en stall.

Tips: En property av typen int ved navn “Id” vil automatisk bli primærnøkkel, not null og auto increment.

Tips: Bruk virtual for å få lazy loading av objekter på fremmednøkkel, eller enforce loading ved hjelp av .Include(“<ENTITET>”) når man skriver LINQ-spørringer.

I teorien kan vi nå få tak i felter og fremmednøkler slik:

Et stall-objekt:
Stable stable = new Stable();

Collection med hester:
ICollection<Horse> horses = stable.Horses;

Print ut navn på alle hester i stallen til console:

foreach(var horse in horses)
{
  Console.WriteLine(horse.Name);
}

 

Database

For å få generert tabeller i databasen vår fra modellene vi har definert, gjør vi følgende:

public class HorseContext : DbContext {
  public DbSet<Stable> Stables { get; set; }
  public DbSet<Horse> Horses { get; set; }
}

 
Dette forteller Entity Framework om mappingen mellom databasen og modellene våre. Den vil autogenerere tabellene ved første kjøring.

Om man vil kjøre spørringer til databasekonteksten vår, oppretter man bare et objekt:
HorseContext _db = new HorseContext();

Gir deg alle stallobjekter:
_db.Stable.ToList();

Hent alle hester i stallen ved navn “Unstable”:
_db.Horse.Where(x => x.Stable.Name == “Unstable”).ToList();

Tips: Det kan være lurt å bruke f.eks. et repository-pattern e.l. som et lag oppå databasekonteksten for å kunne skrive tester som ikke går rett på databasen.

Migrasjoner

Hva skjer om vi gjør en endring i en av modellene? La oss si at vi vil legge til streng-feltet “Address” på staller:

Legg til property i modell:
public string Address { get; set; }

Modellen vil nå oppfatte endringen i hele kodebasen, men dette er ikke reflektert i databasen. Til dette bruker vi noe som heter migrasjons-script. Dette er i hovedsak autogenererte script som reflekterer endringer i databasen. Disse har både en up()- og down()-metode slik at det er enkelt å rulle tilbake endringer ved behov.

For å slå på migrasjoner, åpne Package Manager Console i Visual Studio og kjør kommandoen “enable-migrations”

For å lage en ny migrasjon ved endring: “add-migration AddedAddressToStable”

For å oppdatere databasen til nyeste migrasjon “update-database”

På denne måten har man stort sett alltid kontroll på hva som har blitt gjort med databasen, man får full historikk, og dette gjør det selvsagt mye lettere når man arbeider flere utviklere på ett prosjekt.

Tips: Man kan også få migreringer til å kjøre automatisk, men dette kan fort føre til at det skjer noe galt. Det er lurt å se igjennom migrasjonsscriptet som ble generert før man velger å kjøre det. Det er ikke alltid det genererte scriptet er optimalt.

Modellvalidering

Man vil gjerne sette litt restriksjoner på feltene sine. Dette kan man enkelt gjøre ved å dekorere properties med annotasjoner:

[Required(ErrorMessage = “Stallen må ha et navn”)]
[MaxLength(50, ErrorMessage = “Navnet kan ikke overskride 50 tegn”)]
public string Name { get; set; }

 
Denne valideringen kan typisk brukes i et view for å gi brukeren tilbakemelding, og i MVC versjon 4 er det faktisk mulig med javascript-plugins å få klientside-validering på modellfeltene!

Oppsummering

Jeg har nå forklart litt om idéen rundt Code-first i MVC med EF. Jeg synes dette er en fin og effektiv måte å jobbe på, hverfall for nye prosjekter. Code-first-metoden finner vi også i andre MVC-rammeverk som Django(Python), og kan derfor være greit å kjenne til. Databaselaget blir i stor grad abstrahert på godt og vondt. Erfaringer jeg har gjort er at man til tider må inn i databasen for å faktisk se med egne øyne hva som faktisk foregår, men dette blir det mindre og mindre av etterhvert som man blir flinkere på konvensjoner og måter å gjøre ting på.

Har du erfaringer med Code-first, lenker til god dokumentasjon, eller spørsmål? Bruk gjerne kommentarfeltet under!
For å lese mer om hva vi i .NET-castle lager i sommer, sjekk ut Kristian sitt innlegg.

Summer Intern i Visma Consulting. Studerer informatikk ved NTNU.
Kontakt Christian Strand: