In this tutorial, you will be able to create a simple ASP.NET Core MVC app working with Docker. But before starting code, let’s talk about these technologies: I’m sure, you have listened about .NET Framework, but what about .NET Core? What about Docker? Well, it’s time to put all on site.
That answer can be found at Microsoft’s .NET home page, which gives us the next definition:
.NET is a free, cross-platform, open source developer platform for building many different types of applications. With .NET, you can use multiple languages, editors, and libraries to build for web, mobile, desktop, gaming, and IoT.
.NET has different implementations depending on the Operating System (Windows, Linux, MacOS, iOS, Android, UWP) and the target (server, desktop or mobile). We have 3 implementations:
If you’re an observer and you have looked at the image, I’m sure you’re wondering what is .NET Standard: Immo Landwerth (program manager on the .NET Framework team at Microsoft) writes in his blog:
.NET Standard solves the code sharing problem for .NET developers across all platforms by bringing all the APIs that you expect and love across the environments that you need: desktop applications, mobile apps & games and cloud services.
In the past, we only had a .NET Framework that is mainly a large and monolithic framework that has to be installed in full per machine (or you can’t use it) and allows the creation of console, desktop, web and mobile applications, where they run only on Windows.
At present, .NET Core is the new generation of .NET technologies and unlike its traditional version, .NET Core is based and developed with the following goals in mind:
Now we know that .NET Core is a powerful implementation of the .NET platform that allows us to develop, for example, a modern cross-platform web app with the C# program language on a Linux OS! Can you believe it?
With Docker, you will never say the phrase “but it worked in my computer!”, because you can package all dependencies together with the software code to make your app working in a container and run it everywhere!
Docker is not precisely a Virtual Machine. Virtual machines are a sandboxed environment, which contains an entire computer with its virtual hardware, OS and software; booting up a virtual machine can sometimes take a few minutes to boot up.
Containers are a lightweight alternative to full machine virtualization since they are commonly used to sandbox a single application. Containers use the host OS’ kernel, and thus no bootup time is needed. So, containers are faster than Virtual Machines.
Containers, containers, containers… What is that and why it’s so important? Well, here is the core of Docker: Containers are standardized units that have everything the software needs to run including libraries, system tools, code, and runtime. Docker packages software into containers that lets you quickly deploy and scale applications into any environment and know your code will run.
How can we run our containers? To do that, we need images. An image is an executable package that includes everything needed to run an application: the code, a runtime, libraries, environment variables, and configuration files. So, we can say that a container is a runtime instance of an image. Another important thing about images is that we have available to download official images in the repositories that Docker offers at Docker Hub, which is a cloud-based registry service that includes private and public image repositories.
As you saw at the beginning of this tutorial, we’re going to use a Linux OS. In my case, I will be using Manjaro, which is an Arch-based Linux distro. But don’t worry: the only difference is mainly the installation phase of .NET Core and Docker. If you’re using Windows/macOS, you can still follow me!
OK! First of all, we need to install .NET Core SDK and Docker in our machine (links under requirements). For linux users, we have this page to install Docker with depending on your Linux distro.
Manjaro (Arch and Antergos) users have available in the AUR repository all we need:
$ sudo pacman -S dotnet-runtime dotnet-sdk docker
Now check if everything was installed correctly. You have to see some info about .NET Core and Docker:
$ dotnet
$ docker
If you have problems with docker, be sure that the service is turned on:
$ sudo systemctl status docker
If the service it’s OK, try to put “docker info”. If you read “cannot connect to the Docker daemon” or similar, maybe your user is not included in the Docker group:
# groupadd docker
# usermod -aG docker ${USER}
In your terminal, run the following commands:
$ dotnet new mvc -o awesomeMVC --no-https
$ cd awesomeMVC
What do these commands mean?
dotnet new command creates a new application.mvc parameter selects what template to use when creating your app.-o parameter creates a directory named awesomeMVC where your app is stored.--no-https flag specifies not to enable HTTPS.What files were created?
Startup.cs contains all the settings and configurations.awesomeMVC/Views directory contains some example web pages for the application.awesomeMVC.csproj defines what libraries are referenced, etc.Can we run our app? YES! In your terminal, run the following command:
$ dotnet run
Once the command completes, browse to http://localhost:5000/
Yaaaay! It works 😃
Let’s make some changes: open Views/Home/Index.cshtml in VS Code and replace all of the code with the following:
@{
ViewData[“Title”] = “Home Page”;
}
<div class=”text-center”>
<img class=”image-responsive”
src="https://nunit.org/img/dotnetfoundation.png"
height=”200em”>
<p>The time on the server is @DateTime.Now</p>
</div>
Save and refresh the browser.
It looks better!
How can we do that? First, we have to know that:
Docker can build images automatically by reading the instructions from a “Dockerfile”. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.
So, in the project’s folder, create a file named “Dockerfile” and add the text below. The Dockerfile assumes that your application is called awesomeMVC. Change the Dockerfile to use the DLL file of your project.
# Take a base image from the public Docker Hub repositories
FROM microsoft/dotnet:sdk AS build-env
# Navigate to the “/app” folder (create if not exists)
WORKDIR /app
# Copy csproj and download the dependencies listed in that file
COPY *.csproj ./
RUN dotnet restore
# Copy all files in the project folder
COPY . ./
RUN dotnet publish -c Release -o out
# Build runtime image
FROM microsoft/dotnet:aspnetcore-runtime
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT [“dotnet”, “awesomeMVC.dll”]
To make your build context as small as possible add a .dockerignore file to your project folder and copy the following into it.
bin\
obj\
Build and run the Docker image
Open a terminal and navigate to your project folder. Use the following commands to build and run your Docker image:
$ docker build -t awesome-mvc .
$ docker run -d -p 8080:80 -- name myapp awesome-mvc
You can check if the container is active with:
$ docker container ls
docker inspect -f "{{ .NetworkSettings.Networks.nat.IPAddress }}" myappCongratulations! 🎊 You’ve created your first .NET Core app running in a Docker Container! 😄
If you need to run more than one image at the same time and they have to be connected, you need to know about Docker Compose. In the official documentation of Docker says:
Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration.
So, what needs our app? A database? Do you like SQL Server? OK! Let’s see…
First, we have to pull down the image of our database. As we say, we will use SQL Server for Linux:
$ docker pull microsoft/mssql-server-linux
Also, you can use this SQL image: mcr.microsoft.com/mssql/server:latest
Now that we have the image, we need to start it up and connect to it to make sure it is operating as we expect. Let’s run the container:
$ docker run \
-e 'ACCEPT_EULA=Y' \
-e 'SA_PASSWORD=yourStrong(!)Password' \
-p 1433:1433 \
--name sqlserver \
-d microsoft/mssql-server-linux
-e specifies an environment variable, here we specify password and acceptance of EULA (end user license agreement),-p pecifies the port to forward so that we can connect from the host (our local machine),--name specifies the name used to identify the container — this is useful to start/stop/delete the container — ,-d specifies that we want to start a detached container (runs in background).Once that the container is up and running, open *Azure Data Studio so we can connect to the database. Click on “Add Connection”:
Now, complete with localhost as server, sa as username and yourStrong(!)Password as password. Then connect to the db:
Let’s back to our project “awesomeMVC” and we will replace the entire text at awesomeMVC.cproj:
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.2" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.2" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.4" />
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.3" />
</ItemGroup>
Then, install all the dependencies typing this in the terminal:
$ dotnet restore
Great! Now let’s get to creating our models. We are going to model some characters from Resident Evil 4 (man, I like this game!) and we will define items in a single file since it is such a simple example.
Let’s start by creating the model: navigate through the “Models” folder in our project and create a file named Model.cs. Next, enter the following code:
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
namespace awesomeMVC.Models {
public class CharactersContext : DbContext {
public CharactersContext(DbContextOptions<CharactersContext> options) : base(options) {}
public DbSet<Character> Characters { get; set; }
}
public class Character {
public int CharacterId { get; set; }
public string FirstName { get; set; }
public string LastName { get;set; }
}
}
Now we need to wire up the database context with the dependency injection framework. To do this open `Startup.cs`` and add the add the new lines.
First, add the new using statements:
using awesomeMVC.Models;
using Microsoft.EntityFrameworkCore;
With those added to the list of using statements, we can now modify the ConfigureServices method to add our code.
public void ConfigureServices(IServiceCollection services)
{
...
// This is the new code.
var connection = Configuration.GetConnectionString("DefaultDatabase");
services.AddDbContext<CharactersContext>(options => options.UseSqlServer(connection));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
Now let’s add our connection string information to our appsettings.Development.json by adding the following code:
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"ConnectionStrings" : {
"DefaultDatabase": "Server=localhost;Database=RE4Example;User Id=sa;Password=yourStrong(!)Password;"
}
}
With our connection string, the last step to do before we run our migrations is to create our RE4Example database. We can do this with Data Studio. Open it up and click on New Query.
With that window open, type “sqlCre” and a snippet will pop, just hit enter.
Now we are ready to create our Entity Framework migrations and create our tables. Run the following command to create the migration.
$ dotnet ef migrations add InitialCreate
If was succesful, you have to see something similar to this:
Now run the migration to create the tables.
$ dotnet ef database update
If ran successfully, let’s use Azure Data Studio to verify it:
Now we just need to create our controller and views. We are going to use scaffolding for this tutorial because this isn’t the interesting part:
$ dotnet tool install -g dotnet-aspnet-codegenerator
$ dotnet restore
Now we can generate our controllers and views with the scaffolding tools.
$ dotnet aspnet-codegenerator controller \
-name CharactersController \
-m Character \
-dc CharactersContext \
-outDir Controllers \
-udl \
-scripts
If you don’t understand what you’re doing, here’s a reference:
--controllerName|-name : Name of the controller
--model|-m : Model class to use
--dataContext|-dc : DbContext class to use
--referenceScriptLibraries|-scripts : Switch to specify whether to reference script libraries in the generated views
--useDefaultLayout|-udl : Switch to specify that default layout should be used for the views
--relativeFolderPath|-outDir : Specify the relative output folder path from project where the file needs to be generated, if not specified, file will be generated the project folder
Now let’s run the application.
$ dotnet run
Navigate to https://localhost:5000/characters and you should see this:
Now add a few Characters and let’s see if they show up in the SQL Server running on Linux in a docker container:
Characters in the database:
IT WORKS! 🙌 🙌
Now, we’re going to dockerize everything. To do so, we have to use Docker Compose. We need these files:
appsettings.Development.json:“DefaultDatabase”: “Server=db;Database=RE4Example;User Id=sa;Password=yourStrong(!)Password;”
Now, edit the Dockerfile with the next code:
FROM microsoft/dotnet:latest
COPY . /app
WORKDIR /app
RUN ["dotnet", "restore"]
RUN ["dotnet", "build"]
EXPOSE 80/tcp
RUN chmod +x ./entrypoint.sh
CMD /bin/bash ./entrypoint.sh
Create a file named entrypoint.sh and paste this:
#!/bin/bash
set -e
run_cmd="dotnet run --urls http://*:80"
until dotnet ef database update; do
>&2 echo "SQL Server is starting up"
sleep 1
done
>&2 echo "SQL Server is up - executing command"
exec $run_cmd
Create a docker-compose.yml file and write the following code:
version: "3.4"
services:
web:
build: .
ports:
- "8000:80"
depends_on:
- db
db:
image: microsoft/mssql-server-linux
environment:
- SA_PASSWORD=Password=yourStrong(!)Password
- ACCEPT_EULA=Y
ports:
"5433:1433"
That is it! we just need to build this solution in Docker and then run it:
$ docker-compose build
$ docker-compose up
Then, go to http://localhost:8000/characters on your favorite browser.
You can also connect to the database in the localhost,5433 server:
Congratulations! Your app .NET Core app with SQL Server is working thanks to Docker Compose!
If you can’t wait, you can clone and run this project in this github repository.
Thanks for reading! 💚
entrypoint.sh file, maybe are compatibility problems between Windows and Linux scripts. Try the next command and rebuild: dos2linux entrypoint.shHost ASP.NET Core in Docker containers | Microsoft Docs
Build a real-world app with ASP.NET Core and Angular 2
Tutorial: Creating ASP.NET Core + Angular 4 app in Docker container connected to SQL Azure Database — Infopulse
Enter ASP.NET Core 2 and Angular 5 Applications with Docker
What is .NET?
Introduction to ASP.NET Core | Microsoft Docs
.NET architectural components | Microsoft Docs
Announcing .NET Standard 2.0
EF Core tools reference (.NET CLI) — EF Core | Microsoft Docs
ASP.NET Tutorial | Hello World in 10 minutes | .NET
Dockerize a .NET Core application | Docker Documentation
Quickstart: Compose and ASP.NET Core with SQL Server | Docker Documentation
.NET Core, .NET Framework, Xamarin — The “WHAT and WHEN to use it” — Cesar de la Torre [Microsoft] — BLOG
.NET Core vs .NET Framework — Wolox — Medium
Fix: Cannot Connect to the Docker Daemon — Appuals.com
Jamie Phillips — Working with SQL Server on Linux for .NET Development Code Generator Error Creating Controller — Developer Community
Are shell scripts sensitive to encoding and line endings?