Mobiele apps programmeren met Xamarin – Entity Framework – Migratie – Xamarin

print

Deze handleiding maakt deel uit van het programmeertraject:


Inhoud


Wat vooraf ging


Inleiding

We hebben, in vorige handleidingen, een heel eenvoudig Xamarin Entity Framework project uitgewerkt.

Stel dat er nooit nog iets kan en zal wijzigen aan de modellen, de databank, dan kan die app zo gepubliceerd worden en hoeft u niet verder te lezen.

Herinnert u zich nog dat we, toen we een basistemplate voor een Entity Framework app ontworpen hebben dat we een Console-project hadden toegevoegd met de naam Migratie?

Ik herneem even die code, normaal is dit reeds toegevoegd en moet u het niet hernemen.

Migratie voorbereiden

Migratie is de methode die Entity Framework gebruikt om, na publicatie, wijzigingen aan de databank door te voeren, met behoud van de reeds toegevoegde gegevens of anders gezegd, het synchroniseren tussen het Model en de databank met behoudt van de ingevoerde gegevens.

Migration is a way to keep the database schema in sync with the EF Core model by preserving data.

Op het ogenblik van dit schrijven is directe migratie in een Xamarin project niet mogelijk (Microsoft is zich bewust van dit probleem en wellicht komt er een gemakkelijkere oplossing in een volgende versie van Entity Framework).

Omdat directe migratie niet mogelijk is moeten we een kleine omweg maken. Die omweg kan via een Console App (.NET Core) project.

  • Klik rechts op de Solution en kies voor AddNew Project….
  • Kies voor .NET CoreConsole App (.NET Core) en geef een passende naam (bv. Migratie).

Vervolgens moeten er een referentie gelegd worden naar het basis, portable project.

  • Klik rechts op het Console App (.NET Core) project MigratieDependenciesAdd Reference….

  • Vink het basis/portable project aan (hier TestEF) en druk op OK.

Het Console (.NET Core) project is toegevoegd en de referentie gelegd, het staat klaar en we komen hier later op terug.

Oké, de voorbereidingen zijn getroffen, laat ons het probleem eerst schetsen en nadien oplossen.


Probleem

Stel echter dat u na publicatie toch nog veranderingen wilt aanbrengen aan het Model, een attribuut toevoegen. Bijvoorbeeld een Omschrijving toevoegen aan het model Blog.

Laten we testen wat er gebeurt.

  • Start het project (van de vorige handleidingen).
  • Open, in de folder Models, Blog.cs.
  • Voeg het attribuut Omschrijving toe.
using System.Collections.Generic;

namespace TestEF.Models
{
    public class Blog
    {
        public int BlogId { get; set; }
        public string Name { get; set; }
        public string Url { get; set; }
        public string Omschrijving { get; set; }

        public List<Post> Posts { get; set; }
    }
}
  • Geef een record een waarde voor Omschrijving.
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;
using TestEF.Models;

namespace TestEF.Services
{
    public class DatabaseContext : DbContext
    {
        string _dbPath;

        public DatabaseContext(string dbPath)
        {
            _dbPath = dbPath;
            //Database.EnsureDeleted();
            Database.EnsureCreated();
        }

        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite($"Filename={_dbPath}");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //Name
            modelBuilder.Entity<Blog>()
                .Property(b => b.Name)
                .IsRequired();

            modelBuilder.Entity<Blog>()
                .Property(b => b.Name)
                .HasMaxLength(100);

            //URL
            modelBuilder.Entity<Blog>()
                .Property(b => b.Url)
                .IsRequired();

            modelBuilder.Entity<Blog>()
                .Property(b => b.Url)
                .HasMaxLength(500);

            //Title Post
            modelBuilder.Entity<Post>()
                .Property(p => p.Title)
                .IsRequired();

            modelBuilder.Entity<Post>()
                .Property(p => p.Title)
                .HasMaxLength(100);


            modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 1, Name="PCVO Groeipunt", Url = "https://www.pcvogroeipunt.be", Omschrijving = "Test" });
            modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 2, Name="ICT-opleidingen", Url = "https://ictopleidingen.azurewebsites.net" });

            modelBuilder.Entity<Post>().HasData(
               new { BlogId = 1, PostId = 1, Title = "Post 1", Content = "Test 1" },
               new { BlogId = 1, PostId = 2, Title = "Post 2", Content = "Test 2" },
               new { BlogId = 2, PostId = 3, Title = "Post 3", Content = "Test 3" },
               new { BlogId = 2, PostId = 4, Title = "Post 4", Content = "Test 4" });
        }
    }
}
  • Start het project. U krijgt volgende fout:

Hoewel de “kolom” toegevoegd is binnen het Model blijkt hij niet te bestaan in de databank!


Oorzaak

De oorzaak ligt hem in de DatabaseContext:

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;
using TestEF.Models;

namespace TestEF.Services
{
    public class DatabaseContext : DbContext
    {
        string _dbPath;

        public DatabaseContext(string dbPath)
        {
            _dbPath = dbPath;
            //Database.EnsureDeleted();
            Database.EnsureCreated();
        }

        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite($"Filename={_dbPath}");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //Name
            modelBuilder.Entity<Blog>()
                .Property(b => b.Name)
                .IsRequired();

            modelBuilder.Entity<Blog>()
                .Property(b => b.Name)
                .HasMaxLength(100);

            //URL
            modelBuilder.Entity<Blog>()
                .Property(b => b.Url)
                .IsRequired();

            modelBuilder.Entity<Blog>()
                .Property(b => b.Url)
                .HasMaxLength(500);

            //Title Post
            modelBuilder.Entity<Post>()
                .Property(p => p.Title)
                .IsRequired();

            modelBuilder.Entity<Post>()
                .Property(p => p.Title)
                .HasMaxLength(100);


            modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 1, Name="PCVO Groeipunt", Url = "https://www.pcvogroeipunt.be", Omschrijving = "Test" });
            modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 2, Name="ICT-opleidingen", Url = "https://ictopleidingen.azurewebsites.net" });

            modelBuilder.Entity<Post>().HasData(
               new { BlogId = 1, PostId = 1, Title = "Post 1", Content = "Test 1" },
               new { BlogId = 1, PostId = 2, Title = "Post 2", Content = "Test 2" },
               new { BlogId = 2, PostId = 3, Title = "Post 3", Content = "Test 3" },
               new { BlogId = 2, PostId = 4, Title = "Post 4", Content = "Test 4" });
        }
    }
}

Database.EnsureCreated(); zorgt ervoor dat de databank zeker aangemaakt wordt, indien ze nog niet zou bestaan, maar eens de databank bestaat wordt deze verder niet meer bekeken, laat staan aangepast.

Oké, laten we dan gewoon eerst de databank verwijderen met Database.EnsureDeleted();, zodat ze niet meer bestaat en ze dan opnieuw aanmaken.

Dit is een oplossing zolang u in testfase bent, maar eens in productie zorgt het verwijderen van de databank ervoor dat alle opgeslagen gegevens eveneens verwijderd zijn en dit is onaanvaardbaar voor een databank in productie.

De oplossing ligt in Migratie.

Vervang Database.EnsureCreated(); door Database.Migrate(); of zo u wenst door Database.MigrateAsync();

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;
using TestEF.Models;

namespace TestEF.Services
{
    public class DatabaseContext : DbContext
    {
        string _dbPath;

        public DatabaseContext(string dbPath)
        {
            _dbPath = dbPath;
            Database.Migrate();
        }

        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite($"Filename={_dbPath}");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //Name
            modelBuilder.Entity<Blog>()
                .Property(b => b.Name)
                .IsRequired();

            modelBuilder.Entity<Blog>()
                .Property(b => b.Name)
                .HasMaxLength(100);

            //URL
            modelBuilder.Entity<Blog>()
                .Property(b => b.Url)
                .IsRequired();

            modelBuilder.Entity<Blog>()
                .Property(b => b.Url)
                .HasMaxLength(500);

            //Title Post
            modelBuilder.Entity<Post>()
                .Property(p => p.Title)
                .IsRequired();

            modelBuilder.Entity<Post>()
                .Property(p => p.Title)
                .HasMaxLength(100);


            modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 1, Name="PCVO Groeipunt", Url = "https://www.pcvogroeipunt.be", Omschrijving = "Test" });
            modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 2, Name="ICT-opleidingen", Url = "https://ictopleidingen.azurewebsites.net" });

            modelBuilder.Entity<Post>().HasData(
               new { BlogId = 1, PostId = 1, Title = "Post 1", Content = "Test 1" },
               new { BlogId = 1, PostId = 2, Title = "Post 2", Content = "Test 2" },
               new { BlogId = 2, PostId = 3, Title = "Post 3", Content = "Test 3" },
               new { BlogId = 2, PostId = 4, Title = "Post 4", Content = "Test 4" });
        }
    }
}
  • Start het project.

En… nog steeds dezelfde fout. Tja, had het zo simpel geweest ik had deze handleiding niet moeten schrijven.

Werkt migratie dan niet?

Toch wel, maar u moet ze voorbereiden en eigenlijk handmatig uitvoeren.

Ik geef toe, de werkwijze is allesbehalve eenvoudig, Microsoft is zich hiervan bewust en geruchten gaan dat ze werken aan een automatisch migratie, maar momenteel is er nog een stukje handwerk. Laten we eraan beginnen.


Eerste migratie

Eerst, belangrijk!

  • Verwijder de net toegevoegde kolom Omschrijving opnieuw en Build het programma opnieuw zodat we zeker zijn van een succesvolle Build (en we dus niet langer de fout krijgen)!

Nu we een succesvolle Build hebben kunnen we beginnen aan de Migratie.

  • Kopieer alle Modellen en de Databasecontext naar die Console app Migratie.

  • Open DabaseContext.cs uit het Migratie-project.
  • Verwijder (of plaats als commentaar) de Constructor (anders krijgt u later een foutmelding).
  • Verwijder eveneens de testgegevens die u reeds hebt toegevoegd via code.

De bijgewerkte DatabaseContect.cs:

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;
using TestEF.Models;

namespace TestEF.Services
{
    public class DatabaseContext : DbContext
    {
        string _dbPath;

        public DatabaseContext(string dbPath)
        {
            _dbPath = dbPath;
            Database.Migrate();
        }

        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite($"Filename={_dbPath}");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //Name
            modelBuilder.Entity<Blog>()
                .Property(b => b.Name)
                .IsRequired();

            modelBuilder.Entity<Blog>()
                .Property(b => b.Name)
                .HasMaxLength(100);

            //URL
            modelBuilder.Entity<Blog>()
                .Property(b => b.Url)
                .IsRequired();

            modelBuilder.Entity<Blog>()
                .Property(b => b.Url)
                .HasMaxLength(500);

            //Title Post
            modelBuilder.Entity<Post>()
                .Property(p => p.Title)
                .IsRequired();

            modelBuilder.Entity<Post>()
                .Property(p => p.Title)
                .HasMaxLength(100);


            modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 1, Name="PCVO Groeipunt", Url = "https://www.pcvogroeipunt.be", Omschrijving = "Test" });
            modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 2, Name="ICT-opleidingen", Url = "https://ictopleidingen.azurewebsites.net" });

            modelBuilder.Entity<Post>().HasData(
               new { BlogId = 1, PostId = 1, Title = "Post 1", Content = "Test 1" },
               new { BlogId = 1, PostId = 2, Title = "Post 2", Content = "Test 2" },
               new { BlogId = 2, PostId = 3, Title = "Post 3", Content = "Test 3" },
               new { BlogId = 2, PostId = 4, Title = "Post 4", Content = "Test 4" });
        }
    }
}

U kunt nu de eigenlijke migratiecommando’s uitvoeren via:

  • Package Manager Console
  • Windows PowerShell Console

Ik kies voor Windows PowerShell. Weet dat u de commando’s moet uitvoeren vanuit de migratiefolder.

  • Klik rechts op het Migratie-project en kies voor Open Folder in File Explorer om de Verkenner te openen in de gewenste map.

  • In de Verkenner klikt u op BestandWindows Powershell openenWindows PowerShell openen.

  • Voer de volgende code in: dotnet ef migrations add Migratie1, hierbij is Migratie1 een zelfgekozen naam.

Merk op dat u de migratie weer kunt verwijderen met dontnet ef migrations remove. Het updaten, na dat de migratie is toegevoegd, van de databank kan via dotnet ef database update.

Met een beetje geluk, want dat hebt u altijd wel nodig in het leven, is de migratie uitgevoerd en zijn volgende mappen automatisch aangemaakt.

Deze bestanden bevatten de nodige informatie, gehaald uit de Modellen en de DatabaseContext, om de tabellen aan te maken:

Migratie1.cs

Het hoofdmigratiebestand met migratiebewerkingen in de methoden Up() en Down(). De methode Up() bevat de code voor het maken van databankobjecten en de methode Down() bevat code voor het verwijderen van databankobjecten. Het cijfertje vooraan is de datum en tijd van creatie.

using Microsoft.EntityFrameworkCore.Migrations;

namespace Migratie.Migrations
{
    public partial class Migratie1 : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Blogs",
                columns: table => new
                {
                    BlogId = table.Column<int>(nullable: false)
                        .Annotation("Sqlite:Autoincrement", true),
                    Name = table.Column<string>(maxLength: 100, nullable: false),
                    Url = table.Column<string>(maxLength: 500, nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Blogs", x => x.BlogId);
                });

            migrationBuilder.CreateTable(
                name: "Posts",
                columns: table => new
                {
                    PostId = table.Column<int>(nullable: false)
                        .Annotation("Sqlite:Autoincrement", true),
                    Title = table.Column<string>(maxLength: 100, nullable: false),
                    Content = table.Column<string>(nullable: true),
                    BlogId = table.Column<int>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Posts", x => x.PostId);
                    table.ForeignKey(
                        name: "FK_Posts_Blogs_BlogId",
                        column: x => x.BlogId,
                        principalTable: "Blogs",
                        principalColumn: "BlogId",
                        onDelete: ReferentialAction.Cascade);
                });

            migrationBuilder.CreateIndex(
                name: "IX_Posts_BlogId",
                table: "Posts",
                column: "BlogId");
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Posts");

            migrationBuilder.DropTable(
                name: "Blogs");
        }
    }
}

Migratie1.designer.cs

Het migratie metadata-bestand dat informatie bevat die door EF Core wordt gebruikt. Het cijfertje vooraan is de datum en tijd van creatie.

// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using TestEF.Services;

namespace Migratie.Migrations
{
    [DbContext(typeof(DatabaseContext))]
    [Migration("20181123120339_Migratie1")]
    partial class Migratie1
    {
        protected override void BuildTargetModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("ProductVersion", "2.1.4-rtm-31024");

            modelBuilder.Entity("TestEF.Models.Blog", b =>
                {
                    b.Property<int>("BlogId")
                        .ValueGeneratedOnAdd();

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasMaxLength(100);

                    b.Property<string>("Url")
                        .IsRequired()
                        .HasMaxLength(500);

                    b.HasKey("BlogId");

                    b.ToTable("Blogs");
                });

            modelBuilder.Entity("TestEF.Models.Post", b =>
                {
                    b.Property<int>("PostId")
                        .ValueGeneratedOnAdd();

                    b.Property<int>("BlogId");

                    b.Property<string>("Content");

                    b.Property<string>("Title")
                        .IsRequired()
                        .HasMaxLength(100);

                    b.HasKey("PostId");

                    b.HasIndex("BlogId");

                    b.ToTable("Posts");
                });

            modelBuilder.Entity("TestEF.Models.Post", b =>
                {
                    b.HasOne("TestEF.Models.Blog", "Blog")
                        .WithMany("Posts")
                        .HasForeignKey("BlogId")
                        .OnDelete(DeleteBehavior.Cascade);
                });
#pragma warning restore 612, 618
        }
    }
}

DatabaseContextModelSnapshot.cs

Een momentopname van uw huidige model. Dit wordt gebruikt om te bepalen wat er is gewijzigd bij het maken van de volgende migratie.

// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using TestEF.Services;

namespace Migratie.Migrations
{
    [DbContext(typeof(DatabaseContext))]
    partial class DatabaseContextModelSnapshot : ModelSnapshot
    {
        protected override void BuildModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("ProductVersion", "2.1.4-rtm-31024");

            modelBuilder.Entity("TestEF.Models.Blog", b =>
                {
                    b.Property<int>("BlogId")
                        .ValueGeneratedOnAdd();

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasMaxLength(100);

                    b.Property<string>("Url")
                        .IsRequired()
                        .HasMaxLength(500);

                    b.HasKey("BlogId");

                    b.ToTable("Blogs");
                });

            modelBuilder.Entity("TestEF.Models.Post", b =>
                {
                    b.Property<int>("PostId")
                        .ValueGeneratedOnAdd();

                    b.Property<int>("BlogId");

                    b.Property<string>("Content");

                    b.Property<string>("Title")
                        .IsRequired()
                        .HasMaxLength(100);

                    b.HasKey("PostId");

                    b.HasIndex("BlogId");

                    b.ToTable("Posts");
                });

            modelBuilder.Entity("TestEF.Models.Post", b =>
                {
                    b.HasOne("TestEF.Models.Blog", "Blog")
                        .WithMany("Posts")
                        .HasForeignKey("BlogId")
                        .OnDelete(DeleteBehavior.Cascade);
                });
#pragma warning restore 612, 618
        }
    }
}

De eerste, voorbereidende migratie is gedaan, de app kan nu gepubliceerd worden.


Migraties na publicatie

Stel, na de publicaties, u moet toch nog aanpassingen maken aan de databank, velden of tabellen toevoegen. Bijvoorbeeld, die Omschrijving aan de entiteit Blog.

Hoe gaat u nu te werk.

  • Pas het model Blog aan door toevoeging van een Omschrijving.
using System.Collections.Generic;

namespace TestEF.Models
{
    public class Blog
    {
        public int BlogId { get; set; }
        public string Name { get; set; }
        public string Url { get; set; }
        public string Omschrijving { get; set; }

        public List<Post> Posts { get; set; }
    }
}
  • Kopieer het aangepaste Model naar het Migratie-project.
  • Indien u ook aanpassingen gedaan hebt aan de DatabaseContext.cs voegt u deze ook toe aan de DatabaseContext.cs in het Migratie-project.
  • Voer, via Windows Powershell een nieuwe migratie uit, met een nieuwe naam (bv. Migratie2).

U krijgt nu nieuwe bestanden in de Migration-folder:

Migratie2.cs bevat nu enkel de wijzigingen:

using Microsoft.EntityFrameworkCore.Migrations;

namespace Migratie.Migrations
{
    public partial class Migratie2 : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.AddColumn<string>(
                name: "Omschrijving",
                table: "Blogs",
                nullable: true);
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropColumn(
                name: "Omschrijving",
                table: "Blogs");
        }
    }
}

Migratie2.designer.cs

// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using TestEF.Services;

namespace Migratie.Migrations
{
    [DbContext(typeof(DatabaseContext))]
    [Migration("20181123123538_Migratie2")]
    partial class Migratie2
    {
        protected override void BuildTargetModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("ProductVersion", "2.1.4-rtm-31024");

            modelBuilder.Entity("TestEF.Models.Blog", b =>
                {
                    b.Property<int>("BlogId")
                        .ValueGeneratedOnAdd();

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasMaxLength(100);

                    b.Property<string>("Omschrijving");

                    b.Property<string>("Url")
                        .IsRequired()
                        .HasMaxLength(500);

                    b.HasKey("BlogId");

                    b.ToTable("Blogs");
                });

            modelBuilder.Entity("TestEF.Models.Post", b =>
                {
                    b.Property<int>("PostId")
                        .ValueGeneratedOnAdd();

                    b.Property<int>("BlogId");

                    b.Property<string>("Content");

                    b.Property<string>("Title")
                        .IsRequired()
                        .HasMaxLength(100);

                    b.HasKey("PostId");

                    b.HasIndex("BlogId");

                    b.ToTable("Posts");
                });

            modelBuilder.Entity("TestEF.Models.Post", b =>
                {
                    b.HasOne("TestEF.Models.Blog", "Blog")
                        .WithMany("Posts")
                        .HasForeignKey("BlogId")
                        .OnDelete(DeleteBehavior.Cascade);
                });
#pragma warning restore 612, 618
        }
    }
}

DatabaseContextModelSnapshot.cs

// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using TestEF.Services;

namespace Migratie.Migrations
{
    [DbContext(typeof(DatabaseContext))]
    partial class DatabaseContextModelSnapshot : ModelSnapshot
    {
        protected override void BuildModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("ProductVersion", "2.1.4-rtm-31024");

            modelBuilder.Entity("TestEF.Models.Blog", b =>
                {
                    b.Property<int>("BlogId")
                        .ValueGeneratedOnAdd();

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasMaxLength(100);

                    b.Property<string>("Omschrijving");

                    b.Property<string>("Url")
                        .IsRequired()
                        .HasMaxLength(500);

                    b.HasKey("BlogId");

                    b.ToTable("Blogs");
                });

            modelBuilder.Entity("TestEF.Models.Post", b =>
                {
                    b.Property<int>("PostId")
                        .ValueGeneratedOnAdd();

                    b.Property<int>("BlogId");

                    b.Property<string>("Content");

                    b.Property<string>("Title")
                        .IsRequired()
                        .HasMaxLength(100);

                    b.HasKey("PostId");

                    b.HasIndex("BlogId");

                    b.ToTable("Posts");
                });

            modelBuilder.Entity("TestEF.Models.Post", b =>
                {
                    b.HasOne("TestEF.Models.Blog", "Blog")
                        .WithMany("Posts")
                        .HasForeignKey("BlogId")
                        .OnDelete(DeleteBehavior.Cascade);
                });
#pragma warning restore 612, 618
        }
    }
}
  • Start het project, het zou moeten foutloos werken.

Nog een voorbeeldje, stel, u bent “vergeten” een Model Auteur, toe te voegen. Een Auteur heeft een één op veel relatie met Post (een auteur kan meerdere posts hebben, maar een post heeft maar één auteur (stel)).

Voeg onderstaand Model Auteur toe:

using System;
using System.Collections.Generic;
using System.Text;

namespace TestEF.Models
{
    public class Auteur
    {
        public int AuteurId { get; set; }
        public string Name { get; set; }

        public List<Post> Posts { get; set; }
    }
}
  • Kopieer dit ook naar het Migratie-project.
  • Er dient ook een aanpassing te gebeuren in DatabaseContext.cs, namelijk een toevoeging van public DbSet Auteurs { get; set; }. Doe dit in beide projecten!
  • Voer, via Windows Powershell een nieuwe migratie uit, met een nieuwe naam (bv. Migratie3).

De nodige Migratie-bestanden worden weer aangemaakt.

Migratie3.cs

using Microsoft.EntityFrameworkCore.Migrations;

namespace Migratie.Migrations
{
    public partial class Migratie3 : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.AddColumn<int>(
                name: "AuteurId",
                table: "Posts",
                nullable: true);

            migrationBuilder.CreateTable(
                name: "Auteurs",
                columns: table => new
                {
                    AuteurId = table.Column<int>(nullable: false)
                        .Annotation("Sqlite:Autoincrement", true),
                    Name = table.Column<string>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Auteurs", x => x.AuteurId);
                });

            migrationBuilder.CreateIndex(
                name: "IX_Posts_AuteurId",
                table: "Posts",
                column: "AuteurId");

            migrationBuilder.AddForeignKey(
                name: "FK_Posts_Auteurs_AuteurId",
                table: "Posts",
                column: "AuteurId",
                principalTable: "Auteurs",
                principalColumn: "AuteurId",
                onDelete: ReferentialAction.Restrict);
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropForeignKey(
                name: "FK_Posts_Auteurs_AuteurId",
                table: "Posts");

            migrationBuilder.DropTable(
                name: "Auteurs");

            migrationBuilder.DropIndex(
                name: "IX_Posts_AuteurId",
                table: "Posts");

            migrationBuilder.DropColumn(
                name: "AuteurId",
                table: "Posts");
        }
    }
}

Migratie3.designer.cs

// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using TestEF.Services;

namespace Migratie.Migrations
{
    [DbContext(typeof(DatabaseContext))]
    [Migration("20181123130432_Migratie3")]
    partial class Migratie3
    {
        protected override void BuildTargetModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("ProductVersion", "2.1.4-rtm-31024");

            modelBuilder.Entity("TestEF.Models.Auteur", b =>
                {
                    b.Property<int>("AuteurId")
                        .ValueGeneratedOnAdd();

                    b.Property<string>("Name");

                    b.HasKey("AuteurId");

                    b.ToTable("Auteurs");
                });

            modelBuilder.Entity("TestEF.Models.Blog", b =>
                {
                    b.Property<int>("BlogId")
                        .ValueGeneratedOnAdd();

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasMaxLength(100);

                    b.Property<string>("Omschrijving");

                    b.Property<string>("Url")
                        .IsRequired()
                        .HasMaxLength(500);

                    b.HasKey("BlogId");

                    b.ToTable("Blogs");
                });

            modelBuilder.Entity("TestEF.Models.Post", b =>
                {
                    b.Property<int>("PostId")
                        .ValueGeneratedOnAdd();

                    b.Property<int?>("AuteurId");

                    b.Property<int>("BlogId");

                    b.Property<string>("Content");

                    b.Property<string>("Title")
                        .IsRequired()
                        .HasMaxLength(100);

                    b.HasKey("PostId");

                    b.HasIndex("AuteurId");

                    b.HasIndex("BlogId");

                    b.ToTable("Posts");
                });

            modelBuilder.Entity("TestEF.Models.Post", b =>
                {
                    b.HasOne("TestEF.Models.Auteur")
                        .WithMany("Posts")
                        .HasForeignKey("AuteurId");

                    b.HasOne("TestEF.Models.Blog", "Blog")
                        .WithMany("Posts")
                        .HasForeignKey("BlogId")
                        .OnDelete(DeleteBehavior.Cascade);
                });
#pragma warning restore 612, 618
        }
    }
}

DatabaseContextModelSnapshot.cs

// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using TestEF.Services;

namespace Migratie.Migrations
{
    [DbContext(typeof(DatabaseContext))]
    partial class DatabaseContextModelSnapshot : ModelSnapshot
    {
        protected override void BuildModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("ProductVersion", "2.1.4-rtm-31024");

            modelBuilder.Entity("TestEF.Models.Auteur", b =>
                {
                    b.Property<int>("AuteurId")
                        .ValueGeneratedOnAdd();

                    b.Property<string>("Name");

                    b.HasKey("AuteurId");

                    b.ToTable("Auteurs");
                });

            modelBuilder.Entity("TestEF.Models.Blog", b =>
                {
                    b.Property<int>("BlogId")
                        .ValueGeneratedOnAdd();

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasMaxLength(100);

                    b.Property<string>("Omschrijving");

                    b.Property<string>("Url")
                        .IsRequired()
                        .HasMaxLength(500);

                    b.HasKey("BlogId");

                    b.ToTable("Blogs");
                });

            modelBuilder.Entity("TestEF.Models.Post", b =>
                {
                    b.Property<int>("PostId")
                        .ValueGeneratedOnAdd();

                    b.Property<int?>("AuteurId");

                    b.Property<int>("BlogId");

                    b.Property<string>("Content");

                    b.Property<string>("Title")
                        .IsRequired()
                        .HasMaxLength(100);

                    b.HasKey("PostId");

                    b.HasIndex("AuteurId");

                    b.HasIndex("BlogId");

                    b.ToTable("Posts");
                });

            modelBuilder.Entity("TestEF.Models.Post", b =>
                {
                    b.HasOne("TestEF.Models.Auteur")
                        .WithMany("Posts")
                        .HasForeignKey("AuteurId");

                    b.HasOne("TestEF.Models.Blog", "Blog")
                        .WithMany("Posts")
                        .HasForeignKey("BlogId")
                        .OnDelete(DeleteBehavior.Cascade);
                });
#pragma warning restore 612, 618
        }
    }
}
  • Start het project, het zou moeten foutloos werken en u bent klaar het nodige programmeerwerk uit te voeren op het Model Auteur.

Nogmaals, ik heb nergens beweerd dat dit een leuke, vlotte, ideale werkwijze is, maar voorlopig (?) moeten we het ermee doen.

Wilt u meer weten over andere Migratie-commando’s, lees dan hier en hier verder.

Geef een reactie

  • Abonneer je op deze website d.m.v. e-mail

    Voer je e-mailadres in om je in te schrijven op deze website en e-mailmeldingen te ontvangen van nieuwe berichten.