Showing posts with label OnModelCreating. Show all posts
Showing posts with label OnModelCreating. Show all posts

Monday, September 25, 2023

Code First development with ASP.NET MVC

 In this tutorial, you will develop a data driven web application using ASP.NET MVC, SQL Server, and Entity Framework. We shall use Visual Studio Code for our editor. The data model will be based on a Team/Player relationship in sports. We will use SQL Server running in a Docker Container.

Source code: https://github.com/medhatelmasry/TeamPlayersMvc

Assumptions

It is assumed that you have the following installed on your computer:

  • Visual Studio Code
  • .NET 7.0 SDK
  • Docker Desktop

Visual Studio Code Extension

Add this Visual Studio Code Extension if you do not have it already:

The Data Model

We will use the following class to represent a Team:

public class Team {
    [Key]
    public string? TeamName { get; set; }
    public string? City { get; set; }
    public string? Province { get; set; }
    public string? Country { get; set; }

    public List<Player>? Players { get; set; }
}

The primary key is the TeamName and a team has many players.

The following class represents a Player:

public class Player {
    public int PlayerId { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public string?  Position { get; set; }

    public string? TeamName { get; set; }

    [ForeignKey("TeamName")]
    public Team? Team { get; set; }
}

The primary key is PlayerId and each player must belong to a team.

Getting started

In a working directory, run the following command in a terminal window to create a Razor Pages web application in a folder names TeamPlayers:

dotnet new mvc -f net7.0 -o TeamPlayersMvc

Change to the newly created folder with terminal command:

cd TeamPlayersMvc

For the application to work with SQL Server, we will need to add some packages by running the following commands:

dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.EntityFrameworkCore.Design
 
We will use code generation to scaffold MVC controllers & views. For that purpose, you will also need to add this package:

dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design

If you have not done so already, you will need to globally install the following tools for Entity Framework and Code Generation respectively:

dotnet tool install -g dotnet-aspnet-codegenerator
dotnet tool install -g dotnet-ef

NOTE: If these tools are already installed, run the above commands while replacing ‘install’ with ‘update’ to get the latest version of the tool.

Creating the model classes

Open your app in Visual Studio Code with this command:

code .

Add to the Models Teamclasses Team & Player mentioned under title “The Data Model” above.

The Context Class

We will need to create a database context class to work with relational databases using Entity Framework. To this end, create a Data folder. Inside the Data folder, create a class named ApplicationDbContext with the following code:
public class ApplicationDbContext : DbContext {
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options) {}


    public DbSet<Team>? Teams { get; set; }
    public DbSet<Player>? Players { get; set; }
}

Seeding the database with sample data

It is always useful to have some sample data to visualize what the app does. Therefore, we will create a class dedicated to seeding data. In the Data folder, create a static class named SeedData and add to it the following code that contains sample data for teams and players:

public static class SeedData {
    // this is an extension method to the ModelBuilder class
    public static void Seed(this ModelBuilder modelBuilder) {
        modelBuilder.Entity<Team>().HasData(
            GetTeams()
        );
        modelBuilder.Entity<Player>().HasData(
            GetPlayers()
        );
    }
    public static List<Team> GetTeams() {
        List<Team> teams = new List<Team>() {
            new Team() {    // 1
                TeamName="Canucks",
                City="Vancouver",
                Province="BC",
                Country="Canada",
            },
            new Team() {    //2
                TeamName="Sharks",
                City="San Jose",
                Province="CA",
                Country="USA",
            },
            new Team() {    // 3
                TeamName="Oilers",
                City="Edmonton",
                Province="AB",
                Country="Canada",
            },
            new Team() {    // 4
                TeamName="Flames",
                City="Calgary",
                Province="AB",
                Country="Canada",
            },
            new Team() {    // 5
                TeamName="Leafs",
                City="Toronto",
                Province="ON",
                Country="Canada",
            },
            new Team() {    // 6
                TeamName="Ducks",
                City="Anaheim",
                Province="CA",
                Country="USA",
            },
            new Team() {    // 7
                TeamName="Lightening",
                City="Tampa Bay",
                Province="FL",
                Country="USA",
            },
            new Team() {    // 8
                TeamName="Blackhawks",
                City="Chicago",
                Province="IL",
                Country="USA",
            },
        };

        return teams;
    }

    public static List<Player> GetPlayers() {
        List<Player> players = new List<Player>() {
            new Player {
                PlayerId = 1,
                FirstName = "Sven",
                LastName = "Baertschi",
                TeamName = "Canucks",
                Position = "Forward"
            },
            new Player {
                PlayerId = 2,
                FirstName = "Hendrik",
                LastName = "Sedin",
                TeamName = "Canucks",
                Position = "Left Wing"
            },
            new Player {
                PlayerId = 3,
                FirstName = "John",
                LastName = "Rooster",
                TeamName = "Flames",
                Position = "Right Wing"
            },
            new Player {
                PlayerId = 4,
                FirstName = "Bob",
                LastName = "Plumber",
                TeamName = "Oilers",
                Position = "Defense"
            },
        };

        return players;
    }
}

Note that the SeedData class is static because it contains an extension method named Seed() to ModelBuilder.

The Seed() method needs to be called from somewhere. The most appropriate place is the ApplicationDbContext class. Add the following OnModelCreating() method to the ApplicationDbContext class:

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);
    builder.Entity<Player>().Property(m => m.TeamName).IsRequired();

    builder.Entity<Team>().Property(p => p.TeamName).HasMaxLength(30);

    builder.Entity<Team>().ToTable("Team");
    builder.Entity<Player>().ToTable("Player");

    builder.Seed();

In addition to seeding data, the above code ensures the following:
  • TeamName is required
  • The maximum length of TeamName is 30 characters
  • The names of the tables that get created in the database are Team & Player. Otherwise, the names get created as Teams & Players.

The database

You can use any SQL Server database you wish. In my case, so that this app works on Linux, Windows, Mac Intel, and Mac M1, I will run SQL Server in a Docker container. To run SQL Server in a Docker container, run the following command:

docker run --cap-add SYS_PTRACE -e ACCEPT_EULA=1 -e MSSQL_SA_PASSWORD=SqlPassword! -p 1444:1433 --name azsql -d mcr.microsoft.com/azure-sql-edge

The connection string to the database is setup in the appsettings.json (or appsetting.Development.json) file. Edit this file and make the following highlighted updates to it:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Server=tcp:127.0.0.1,1444;Database=TeamPlayersDB;UID=sa;PWD=SqlPassword!;TrustServerCertificate=True;"
  }
}
 

To make our app work with SQL Server, you will need to add the following code to Program.cs just before “var app = builder.Build();”: 

string connStr = builder.Configuration.GetConnectionString("DefaultConnection")!;
builder.Services.AddDbContext<ApplicationDbContext>(
    options => options.UseSqlServer(connStr)
);

Migrations

We can now create a migration named m1 with:

dotnet ef migrations add M1 -o Data/Migrations

This creates a migrations file in Data/Migrations folder. To execute the migrations and create the database and seed data, run the following command:

dotnet ef database update

If all goes well and no errors are generated, we can assume that a database named TeamPlayersDB was created, and data is seeded into tables Team & Player.

NOTE: If you wish to drop the database for whatever reason, you can run command: dotnet ef database drop
 
This is what the tables in the database look like:

Scaffolding Teams & Players controller and views

To incorporate pages into our app that allow us to manage Team & Player data, we will scaffold the necessary controller & views using the aspnet-codegenerator utility. Run the following command from a terminal window in the root of the project to generate files pertaining to teams and players respectively:

dotnet aspnet-codegenerator controller -name TeamsController -outDir Controllers -m Team -dc ApplicationDbContext -udl -scripts

dotnet aspnet-codegenerator controller -name PlayersController -outDir Controllers -m Player -dc ApplicationDbContext -udl -scripts

This produces controllers in the Controllers folder and fiews files in folders Views/Teams & Views/Players respectively. To add menu items on the home page that point to Team & Player pages, edit Views/Shared/_Layout.cshtml and add the following HTML to the <ul> block around line 28:

<li class="nav-item">
    <a class="nav-link text-dark" asp-area="" asp-controller="Teams" asp-action="Index">Teams</a>
</li>

<li class="nav-item">
    <a class="nav-link text-dark" asp-area="" asp-controller="Players" asp-action="Index">Players</a>
</li>

The final product

Run the web app and notice the main menu:




Click on Teams:


Click on Players:


Conclusion

You just learned how to use the code-first database approach with ASP.NET MVC. The same priciples work with ASP.NET Razor Pages. 

Wednesday, January 18, 2023

Code First Development with Razor Pages

In this tutorial, you will develop a data driven web application using ASP.NET Razor Pages, SQL Server, and Entity Framework. We shall use Visual Studio Code for our editor. The data model will be based on a Team/Player relationship in sports. We will use SQL Server running in a Docker Container.

Companion video: https://youtu.be/5ncM-MYQG24

Source code: https://github.com/medhatelmasry/TeamPlayer

Assumptions

It is assumed that you have the following installed on your computer:

  • Visual Studio Code
  • .NET 7.0 SDK
  • Docker Desktop

Visual Studio Code Extension

Add this Visual Studio Code Extension if you do not have it already:

The Data Model

We will use the following class to represent a Team:

public class Team {
    [Key]
    public string? TeamName { get; set; }
    public string? City { get; set; }
    public string? Province { get; set; }
    public string? Country { get; set; }

    public List<Player>? Players { get; set; }
}

The primary key is the TeamName and a team has many players.

The following class represents a Player:

public class Player {
    public int PlayerId { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public string?  Position { get; set; }

    public string? TeamName { get; set; }

    [ForeignKey("TeamName")]
    public Team? Team { get; set; }
}

The primary key is PlayerId and each player must belong to a team.

Getting started

In a working directory, run the following command in a terminal window to create a Razor Pages web application in a folder names TeamPlayers:

dotnet new razor -f net7.0 -o TeamPlayers

Change to the newly created folder with terminal command:

cd TeamPlayers

For the application to work with SQL Server, we will need to add some packages by running the following commands:

dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.EntityFrameworkCore.Design
 
We will use code generation to scaffold razor pages. For that purpose, you will also need to add this package:

dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design

If you have not done so already, you will need to globally install the following tools for Entity Framework and Code Generation respectively:

dotnet tool install -g dotnet-aspnet-codegenerator
dotnet tool install -g dotnet-ef

NOTE: If these tools are already installed, run the above commands while replacing ‘install’ with ‘update’ to get the latest version of the tool.

Creating the model classes

Open your app in Visual Studio Code with this command:

code .

Create a folder named Models and add to it classes Team & Player mentioned under title “The Data Model” above.

The Context Class

We will need to create a database context class to work with relational databases using Entity Framework. To this end, create a Data folder. Inside the Data folder, create a class named ApplicationDbContext with the following code:
public class ApplicationDbContext : DbContext {
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options) {}


    public DbSet<Team>? Teams { get; set; }
    public DbSet<Player>? Players { get; set; }
}

Seeding the database with sample data

It is always useful to have some sample data to visualize what the app does. Therefore, we will create a class dedicated to seeding data. In the Data folder, create a static class named SeedData and add to it the following code that contains sample data for teams and players:

public static class SeedData {
    // this is an extension method to the ModelBuilder class
    public static void Seed(this ModelBuilder modelBuilder) {
        modelBuilder.Entity<Team>().HasData(
            GetTeams()
        );
        modelBuilder.Entity<Player>().HasData(
            GetPlayers()
        );
    }
    public static List<Team> GetTeams() {
        List<Team> teams = new List<Team>() {
            new Team() {    // 1
                TeamName="Canucks",
                City="Vancouver",
                Province="BC",
                Country="Canada",
            },
            new Team() {    //2
                TeamName="Sharks",
                City="San Jose",
                Province="CA",
                Country="USA",
            },
            new Team() {    // 3
                TeamName="Oilers",
                City="Edmonton",
                Province="AB",
                Country="Canada",
            },
            new Team() {    // 4
                TeamName="Flames",
                City="Calgary",
                Province="AB",
                Country="Canada",
            },
            new Team() {    // 5
                TeamName="Leafs",
                City="Toronto",
                Province="ON",
                Country="Canada",
            },
            new Team() {    // 6
                TeamName="Ducks",
                City="Anaheim",
                Province="CA",
                Country="USA",
            },
            new Team() {    // 7
                TeamName="Lightening",
                City="Tampa Bay",
                Province="FL",
                Country="USA",
            },
            new Team() {    // 8
                TeamName="Blackhawks",
                City="Chicago",
                Province="IL",
                Country="USA",
            },
        };

        return teams;
    }

    public static List<Player> GetPlayers() {
        List<Player> players = new List<Player>() {
            new Player {
                PlayerId = 1,
                FirstName = "Sven",
                LastName = "Baertschi",
                TeamName = "Canucks",
                Position = "Forward"
            },
            new Player {
                PlayerId = 2,
                FirstName = "Hendrik",
                LastName = "Sedin",
                TeamName = "Canucks",
                Position = "Left Wing"
            },
            new Player {
                PlayerId = 3,
                FirstName = "John",
                LastName = "Rooster",
                TeamName = "Flames",
                Position = "Right Wing"
            },
            new Player {
                PlayerId = 4,
                FirstName = "Bob",
                LastName = "Plumber",
                TeamName = "Oilers",
                Position = "Defense"
            },
        };

        return players;
    }
}

Note that the SeedData class is static because it contains an extension method named Seed() to ModelBuilder.

The Seed() method needs to be called from somewhere. The most appropriate place is the ApplicationDbContext class. Add the following OnModelCreating() method to the ApplicationDbContext class:

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);
    builder.Entity<Player>().Property(m => m.TeamName).IsRequired();

    builder.Entity<Team>().Property(p => p.TeamName).HasMaxLength(30);

    builder.Entity<Team>().ToTable("Team");
    builder.Entity<Player>().ToTable("Player");

    builder.Seed();

In addition to seeding data, the above code ensures the following:
  • TeamName is required
  • The maximum length of TeamName is 30 characters
  • The names of the tables that get created in the database are Team & Player. Otherwise, the names get created as Teams & Players.

The database

You can use whatever SQL Server database you wish. In my case, so that this app works on Linux, Windows, Mac Intel, and Mac M1, I will run SQL Server in a Docker container. To run SQL Server in a Docker container, run the following command:

docker run --cap-add SYS_PTRACE -e ACCEPT_EULA=1 -e MSSQL_SA_PASSWORD=SqlPassword! -p 1444:1433 --name azsql -d mcr.microsoft.com/azure-sql-edge

The connection string to the database is setup in the appsettings.json file. Edit this file and make the following highlighted updates to it:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Server=tcp:127.0.0.1,1444;Database=TeamPlayersDB;UID=sa;PWD=SqlPassword!;TrustServerCertificate=True;"
  }
}
 

To make our app work with SQL Server, you will need to add the following code to Program.cs just before “var app = builder.Build();”: 

string connStr = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(
    options => options.UseSqlServer(connStr)
);

Migrations

We can now create a migration named m1 with:

dotnet ef migrations add M1 -o Data/Migrations

This creates a migrations file in Data/Migrations folder. To execute the migrations and create the database and seed data, run the following command:

dotnet ef database update

If all goes well and no errors are generated, we can assume that a database named TeamPlayersDB was created, and data is seeded into tables Team & Player.

NOTE: If you wish to drop the database for whatever reason, you can run command: dotnet ef database drop
 
This is what the tables in the database look like:

Scaffolding Teams & Players pages

To incorporate pages into our app that allow us to manage Team & Player data, we will scaffold the necessary pages using the aspnet-codegenerator utility. Run the following command from a terminal window in the root of the project to generate files pertaining to teams and players respectively:

dotnet aspnet-codegenerator razorpage -m Team -dc ApplicationDbContext -udl -outDir Pages/TeamPages --referenceScriptLibraries

dotnet aspnet-codegenerator razorpage -m Player -dc ApplicationDbContext -udl -outDir Pages/PlayerPages --referenceScriptLibraries

This produces files in folders Pages/TeamPages & Pages/PlayerPages respectively. To add menu items on the home page that point to Team & Player pages, edit Pages/Shared/_Layout.cshtml and add the following HTML to the <ul> block around line 25:

<li class="nav-item">
    <a class="nav-link text-dark" asp-area="" asp-page="/TeamPages/Index">Teams</a>
</li>
<li class="nav-item">
    <a class="nav-link text-dark" asp-area="" asp-page="/PlayerPages/Index">Players</a>
</li>

The final product

Run the web app and notice the main menu:


Click on Teams:


Click on Players:


Conclusion

You just learned how to use the code-first database approach with ASP.NET Razor pages. The same priciples work with ASP.NET MVC. 

Saturday, January 15, 2022

Creating a MySQL Database with EF Core in .NET 6.0

In this tutorial I will show how to create a simple .NET 6 console application that interacts with data in MySQL version 8.0.0 using Entity Framework. I will be using the official Connector/NET for Entity Framework driver. The NuGet package is MySql.EntityFrameworkCore Nuget located at https://www.nuget.org/packages/MySql.EntityFrameworkCore/

Instead of installing and running a MySQL instance on my computer, I will run MySQL in a docker container for simplicity.

Source Code: https://github.com/medhatelmasry/MySqlOnFire

Companion Video: https://youtu.be/wGb2IWlZNl4

This article assumes the following:

  1. You have .NET 6.0 installed on your computer. 
  2. You have Docker installed on your computer. 

Setting up the MySQL 8.0.0 container

To download the MySQL version 8.0.0 image from Docker Hub and run it on your local computer, type the following command from within a terminal window:

docker run -p 3333:3306 --name db -e MYSQL_ROOT_PASSWORD=secret -d mysql:8.0.0

This starts a container named 'db' that listens on port 3333 on your local computer. The root password is 'secret'.

To ensure that the MySQL container is running, type the following from within a terminal window:

docker ps

You will see a message similar to the following:

CONTAINER ID   IMAGE         COMMAND                  CREATED        STATUS        PORTS                    NAMES

53e55f4991df   mysql:8.0.0   "docker-entrypoint.s…"   45 hours ago   Up 45 hours   0.0.0.0:3333->3306/tcp   db

Creating a console app

In a working directory, run the following command to create a console application named MySqlOnFire using .NET 6.0:

dotnet new console -f net6.0 -o MySqlOnFire 

Change directory to the newly created folder with:

cd MySqlOnFire

There is only one NuGet package that is needed to talk to MySQL. At the time of writing this article, the version of the MySql.EntityFrameworkCore package that supports .NET 6 is 6.0.0-preview3.1. Add the package by typing the following command from within a terminal window:

dotnet add package MySql.EntityFrameworkCore -v 6.0.0-preview3.1

Model Classes

We will be modeling the following Publisher & Book entities:

In .NET 6.0, we can create a global using file. This helps keep our code minimal. Let us take advantage of this feature. Add a file named GlobalUsings.cs and add to it the following code:

global using System.Text;
global using Microsoft.EntityFrameworkCore; 
global using System.ComponentModel.DataAnnotations.Schema;

Create a file named Publisher.cs and add to it the following class code:

public class Publisher {
    public int PublisherId { get; set; }
    public string? Name { get; set; }
    public virtual ICollection<Book>? Books { get; set; }

    public override string ToString() {
        var txt = new StringBuilder();
        txt.AppendLine($"ID: {PublisherId}");
        txt.AppendLine($"Publisher: {Name}");
        
        return txt.ToString();
    }
}

In the above code, we define the properties of the Publisher class and a ToString() method.

Create another class named Book.cs and add to it the following class code:

public class Book {
    public string? ISBN { get; set; }
    public string? Title { get; set; }
    public string? Author { get; set; }
    public string? Language { get; set; }
    public int Pages { get; set; }

    public int PublisherId { get; set; }

    [ForeignKey("PublisherId")]
    public virtual Publisher? Publisher { get; set; }

    public override string ToString() {
        var txt = new StringBuilder();
        txt.AppendLine($"ISBN: {ISBN}");
        txt.AppendLine($"Title: {Title}");
        txt.AppendLine($"Author: {Author}");
        txt.AppendLine($"Language: {Language}");
        txt.AppendLine($"Pages: {Pages}");
        txt.AppendLine($"Publisher: {Publisher!.Name}");

        return txt.ToString();
    }
}

In the above code, in addition to Book properties, PublisherId is clearly declared as the foreign key and a ToString() method is also defined.

The database context class

Entity Framework requires us to define a DbContext class that becomes the entry point into the database. Therefore, create a new file named LibraryContext.cs and add to it the following code:

public class LibraryContext : DbContext {
   public DbSet<Book>? Books { get; set; }
   public DbSet<Publisher>? Publishers { get; set; }
   protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
     optionsBuilder.UseMySQL("server=localhost;database=library;user=root;port=3333;password=secret");
   }
   protected override void OnModelCreating(ModelBuilder modelBuilder) {
     base.OnModelCreating(modelBuilder);
     modelBuilder.Entity<Publisher>(entity => {
         entity.HasKey(e => e.PublisherId);
         entity.Property(e => e.Name).IsRequired();
     });
     modelBuilder.Entity<Book>(entity => {
         entity.HasKey(e => e.ISBN);
         entity.Property(e => e.Title).IsRequired();
         entity.HasOne(d => d.Publisher)
         .WithMany(p => p!.Books);
     });
     modelBuilder.Entity<Publisher>().HasData(
         new Publisher {
             PublisherId = 1,
             Name = "Mariner Books"
         },
         new Publisher {
             PublisherId = 2,
             Name = "Penguin Books"
         }
     );
     modelBuilder.Entity<Book>().HasData(
         new Book {
             ISBN = "978-0544003415",
             Title = "The Lord of the Rings",
             Author = "J.R.R. Tolkien",
             Language = "English",
             Pages = 1216,
             PublisherId = 1
         },
         new Book {
             ISBN = "978-0547247762",
             Title = "The Sealed Letter",
             Author = "Emma Donoghue",
             Language = "English",
             Pages = 416,
             PublisherId = 1
         },
         new Book {
             ISBN = "978-0143107569",
             Title = "Les Miserables",
             Author = "Victor Hugo",
             Language = "English",
             Pages = 1456,
             PublisherId = 2
         },
         new Book {
             ISBN = "978-0140449174",
             Title = "Anna Karenina",
             Author = "Leo Tolstoy",
             Language = "English",
             Pages = 880,
             PublisherId = 2
         }
     );
   }
}

What does the above code do?

  • Two DbSet objects are defined: Books & Publishers
  • The connection string to our MySQL database running in a docker container is:
server=localhost;database=library;user=root;port=3333;password=secret
  • The OnModelCreating() method establishes the following rules:
    • PublisherId is the primary key for the Publisher entity
    • Name is a required column in the Publisher entity
    • ISBN is the primary key in the Book entity
    • The Book entity has a foreign key into the Publisher entity
  • The OnModelCreating() method also inserts publishers & books seed data

Finally, let us add the code to create the database, seed data, and print data. Replace your Program.cs file with:

CreateDbSeedData();
PrintPublishers();
PrintBooks();
static void CreateDbSeedData() {
    using (var context = new LibraryContext()) {
        // Creates the database if not exists
        context.Database.EnsureCreated();
    }
}

static void PrintBooks() {
    // Gets and prints all books in database
    using (var context = new LibraryContext()) {
        var books = context.Books!
          .Include(p => p.Publisher);
        Console.WriteLine(new string('=', 30));
        foreach (var book in books!) {
            Console.WriteLine(book);
        }
    }
}

static void PrintPublishers() {
    // Gets and prints all books in database
    using (var context = new LibraryContext()) {
        var data = context.Publishers;
        Console.WriteLine(new string('=', 30));
        foreach (var item in data!) {
            Console.WriteLine(item);
        }
    }
}

What does the above code do:

  • The CreateDbSeedData() method creates the database and seeds sample data if it does not already exist.
  • The two print methods (PrintPublishers()PrintBooks()) are self-explanatory.
  • The following methods are called at the top of Program.cs:

CreateDbSeedData();
PrintPublishers();
PrintBooks();

The moment of truth is here. Let us run the application with:

dotnet run

The output should look like this:

==============================
ID: 1
Publisher: Mariner Books

ID: 2
Publisher: Penguin Books

==============================
ISBN: 978-0140449174
Title: Anna Karenina
Author: Leo Tolstoy
Language: English
Pages: 880
Publisher: Penguin Books

ISBN: 978-0143107569
Title: Les Miserables
Author: Victor Hugo
Language: English
Pages: 1456
Publisher: Penguin Books

ISBN: 978-0544003415
Title: The Lord of the Rings
Author: J.R.R. Tolkien
Language: English
Pages: 1216
Publisher: Mariner Books

ISBN: 978-0547247762
Title: The Sealed Letter
Author: Emma Donoghue
Language: English
Pages: 416
Publisher: Mariner Books

 

Peeking inside the MySQL database


You can peek into the MySQL database inside the container. To get into a bash session inside the container, enter the following command:

docker exec -it db bash

This takes you into a a bash session inside the container that looks like this:

root@53e55f4991df:/#

To get into the MySQL command interface, enter the following:

mysql -uroot -psecret

You can check existing databases with:

show databases;

The output will resemble this:

+--------------------+
| Database           |
+--------------------+
| information_schema |
| library            |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

To look into the contents of our library database, run the following MySQL commands:

use library;
show tables;
select * from Publishers;
select * from Books;

To exit from the container interactive session and return to the host operating system, type exit twice.

Cleanup the running MySQL container

You can do the following to stop & remove the MySQL container:

docker rm -f db

Conclusion

This gives you the option to use yet another relational database with your .NET apps. Of course, you can use MySQL with ASP.NET, Blazor, gRPC, desktop or any other types of .NET applications.

References: