Skip to content

MySql Database Continuous Integration for the Dot Net Developer - Part 2 - EF Core Migrations 🦄 🐬

I have seen Demos of Entity Framework migrations before but have never used the technology in a project also the demos I had seen were using MS Sql Server as the Database.

Does EF Core Support Migrations on other Databases ?

Could We use EF Core Migrations as part of Continous Deployment process ?

Could I combine the Unicorn power of EF CORE with the MySql dolphin.

I fired up Visual Studio 2019 and created a dotnet core class library

I then loaded in the following Nuget Packages by running the following command in the Visual Studio Package Manager Console

Install-Package Microsoft.EntityFrameworkCore -Version 3.1.8

Install-Package Microsoft.EntityFrameworkCore.Tools -Version 3.1.8

Install-Package Pomelo.EntityFrameworkCore.MySql -Version 3.1.2

I setup a DbContext and related classes

   public class MySqlDbContext :DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder options)
            => options.UseMySql("Server=localhost;Port=3306;Database=dataMigrate_db;Uid=root;Pwd=itsasecret;");

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<UserPrivilege>()
                .HasKey(c => new {c.UserId, c.PrivilegeId});
        }


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

        public DbSet<Privilege> Privileges { get; set; }

        public DbSet<UserPrivilege> UserPrivileges { get; set; }
    }



    public class Privilege
    {
        public Privilege()
        {
            UserPrivileges = new List<UserPrivilege>();
        }

        public long Id { get; set; }

        public string Name { get; set; }

        public IList<UserPrivilege> UserPrivileges { get; set; }
    }



    public class User
    {
        public User()
        {
            UserPrivileges = new List<UserPrivilege>();
        }

        public long Id { get; set; }

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string Email { get; set; }

        public IList<UserPrivilege> UserPrivileges { get; set; }

    }

    public class UserPrivilege
    {
        public long UserId { get; set; }

        public long PrivilegeId { get; set; }

        public User User { get; set; }
        
        public Privilege Privilege { get; set; }
    }

In the Package Manager Console, I generate the Initial Create Migration

Add-Migration InitialCreate

This produces the following code in a migrations folder

public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Privileges",
                columns: table => new
                {
                    Id = table.Column<long>(nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    Name = table.Column<string>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Privileges", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "Users",
                columns: table => new
                {
                    Id = table.Column<long>(nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    FirstName = table.Column<string>(nullable: true),
                    LastName = table.Column<string>(nullable: true),
                    Email = table.Column<string>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Users", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "UserPrivileges",
                columns: table => new
                {
                    UserId = table.Column<long>(nullable: false),
                    PrivilegeId = table.Column<long>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_UserPrivileges", x => new { x.UserId, x.PrivilegeId });
                    table.ForeignKey(
                        name: "FK_UserPrivileges_Privileges_PrivilegeId",
                        column: x => x.PrivilegeId,
                        principalTable: "Privileges",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_UserPrivileges_Users_UserId",
                        column: x => x.UserId,
                        principalTable: "Users",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                });

            migrationBuilder.CreateIndex(
                name: "IX_UserPrivileges_PrivilegeId",
                table: "UserPrivileges",
                column: "PrivilegeId");
        }

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

            migrationBuilder.DropTable(
                name: "Privileges");

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

I call the Update-Database Command in the Package Manager Console

This produces the following tables in MySql

create table Privileges
(
    Id   bigint auto_increment
        primary key,
    Name longtext null
);

create table Users
(
    Id        bigint auto_increment
        primary key,
    FirstName longtext null,
    LastName  longtext null,
    Email     longtext null
);

create table UserPrivileges
(
    UserId      bigint not null,
    PrivilegeId bigint not null,
    primary key (UserId, PrivilegeId),
    constraint FK_UserPrivileges_Privileges_PrivilegeId
        foreign key (PrivilegeId) references Privileges (Id)
            on delete cascade,
    constraint FK_UserPrivileges_Users_UserId
        foreign key (UserId) references Users (Id)
            on delete cascade
);

create index IX_UserPrivileges_PrivilegeId
    on UserPrivileges (PrivilegeId);

An additional Table was generated called __EFMigrationsHistory this is used by EF Core to track what migrations have been applied to the Database.

At this point it was clear to me that this was not really an option for the team.

There were also some issues with the Data Types. I do not think longtext would be the first choice for string columns if I was writing the tables manually. So I would need to be a bit more explicit in the OnModelCreating method

I could continue to add Migration Classes in C# code. but there was really no point. This was not a DBA Friendly format !

There was no way I could see our DBA team opening up a C# solution and looking at C# Code to determine issues with Query and Index performance

While its was not a choice I could go with. EF Core Migrations is maybe a choice for your project, especially if you are working on a stand alone Desktop app that needs a local database its something to consider.

Sadly there would be no Unicorns and Dolphins this time. I would have to find something else for the team.

References

EF Core Migrations

Code First in EF Core - MySql Documentation

Posted in Development