Opgave: DB - using EF Code First with Migations

Kilder:
https://docs.microsoft.com/en-us/ef/ef6/modeling/code-first/data-annotations
https://www.c-sharpcorner.com/article/dataannotations-in-depth/

I denne opgave tager vi udgangspunkt i eksemplet ItemsRazor ItemRazorV8.zip





I stedet for persistens via Json-filer skal data nu gemmes i en lokal Database ItemDb. Vi vil i denne opgave benytte Entity Framework Code First tilgangen, idet vi allerede har en kørende applikation med eksisterende Model-klasser. Vi vil autogenerere databasen og benytte Migration tool så databasen kan holdes i sync, hvis modellen senere bliver opdateret.


Step 1 (Models-class: Item)
Når Databasens tabeller skal genereres ud fra model-klasserne, gøres det bl.a. på baggrund af DataAnnotations.

  1. Id
    Tilføj følgende DataAnnotations:

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]

    Bemærk:
    Da navnet på key-property'en Id i class Item følger "Convention of Code First" (key skal hedde Id eller ClassnameId), er det ikke nødvendigt at annotere property'en Id med [Key].
    Grunden til annoteringen: [DatabaseGenerated(DatabaseGeneratedOption.None)] er, at Item ikke benytter autogenereret Id (Db skal ikke generere et for os, bruger taster selv)

  2. Name
    Da Name ikke må være null, skal den annoteres med: [Required]
    Hvis Name maks må være 30 karaktere lang annoteres den med: [StringLength(30)]

  3. Price
    Da Price heller ikke må være null, skal den også annoteres med: [Required]


Step 2 (Models-class: User)

  1. UserName
    Da vi antager at UserName er unik (ikke to brugere må have samme username, kan property'en benyttes som primærnøgle.
    UserName følger ikke Conventions og skal annoteres med: [Key]
    UserName må maks være på 10 karakter: [StringLength(10)]

  2. Password
    Da Password ikke må være null, skal den annoteres med: [Required]

     

Step 3 (EF: NuGet Packages)
Da Entity Framework Core ikke er includeret i .Net Core (pr default) skal følgende pakker installeres i projektet:

Step 4 (ItemDbContext)
Tilføj en class ItemDbContext til mappen Models.
ItemsDbContext skal extende class DbContext, override OnConfiguring( ) metoden og have følgende properties DbSet<Item> og DbSet<User>:

public class ItemDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer(@"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=ItemDB; Integrated Security=True; Connect Timeout=30; Encrypt=False");
}

public DbSet<Item> Items { get; set; }
public DbSet<User> Users { get; set; }
}

 

Bemærk: Et alternativ til at override OnConfiguring( ) er at oprette en "ConnectionStrings" i appSettings.json.



Step 5 (Startup.cs)
Da "connectionString" er inkluderet via override af OnConfiguring( ), er det ikke nødvendigt at specificere den i configureringen af Context service i startup filen.
Alt hvad der blot skal tilføjes, er en simpel registrering af service klassen:

services.AddDbContext<ItemDbContext>();

Bemærk: Hvis det ikke er opdateret i filen, så opdater CompabilityVersion.Version fra version 2_2 til 3_0: SetCompatibilityVersion(CompatibilityVersion.Version_3_0);


Migration

Vi vil benytte Migration til at bygge vores Database på baggrund af domain klasserne (Model-klasser)

Step 6 (PMC)

Vi benytter Visual Studio og Package Manager Console Tools til migreringen. Åben PMC med:

Tools -> NuGet Package Manager -> Package Manager Console

  1. Kør kommandoen: Add-Migration ItemRazor

    Bemærk: der dannes en ny mappe med navnet Migrations med Snapshots (til historik)

  2. Kør kommandoen: Update-database

    Bemærk: denne kommando danner selve databasen

  3. Verificer at ItemDb er dannet med tabellerne Items og Users
    Hint: View ->Sql Server Object explorer

 

Step 7 (DbService Get)
Opret en ny klasse DbService i mappen Services.
Klassen skal have metoder der kan hente Items og Users, dvs der skal være metoder ala:

public async Task<List<Item>> GetItems()
{
using (var context = new ItemDbContext())
{
return await context.Items.ToListAsync();
}
}

Bemærk:
Metoderne skal kunne afvikles asynkront, hvorfor de skal være af typen async og returnere et Task-objekt. Der benyttes await da der ikke skal returneres noget før listen er klar.
Items i ItemDbContext er af typen DbSet, hvorfor der konverteres til typen List (og det gøres her async). Bemærk også brugen af using( ...){...} den opretter de nødvendige resourcer og frigiver dem automatisk efter brug.

Step 7 (DbService Save)
Tilføj metoder der kan tilføje nye Items og Users til DbSet i context ala:

public async Task AddItem(Item item)
{
using (var context = new ItemDbContext())
{
context.Items.Add(item);
context.SaveChanges();
}

}

Husk: at kalde SaveChanges efter item/user de er tilføjet, ellers opdateres databasen ikke.

 

Step 8 (Startup.cs)
Registrer den nye service DbService i Startup.cs, så den kan benyttes af ItemService og UserService.

 

Step 9 (UserService og ItemService)
Tilføj referencer til DbService og injicer servicen i konstruktørerne.
Opdater konstruktørerne så klasserne benytter Databasen istedet for Json-filerne.

Bemærk: Der kan "leges" lidt med at indlæse fra json og gemmes i Db første gang (så kan data nemt genbruges)

Step 10 (Afprøv)
Afprøv at applikationen virker.

 

Ekstra opgave:

Step 11 (Generisk DbService)
Gør DbService generisk (ala JsonFileService) - så der ikke er så meget "copy-paste" kode!

 

God fornøjelse!
Henrik Høltzer