Creating .NET options on Linux has at all times been difficult as a result of Microsoft’s Visible Studio requires Home windows so as to work. After engaged on a number of .NET initiatives, I made a decision to check the boundaries of growth of .NET on Linux. This straightforward tutorial focuses on an ASP.NET MVC utility with SQL Server to indicate how elegant and efficient .NET growth could be on my most well-liked OS.
Growth Setting
First, we should make sure the .NET instruments and SDK related to our specific taste of Linux are put in utilizing Microsoft’s normal information.
My most well-liked growth atmosphere consists of a windowed built-in growth atmosphere (IDE), a strong database administration and question device, the database itself, and instruments for constructing and deployment. I exploit the next instruments to attain strong performance and allow a wonderful coding expertise:
Be certain that these instruments are correctly put in earlier than you proceed with our pattern utility.
Venture Scaffolding
On this pattern utility, we’ll spotlight ASP.NET growth and performance via a collection of use circumstances for a hypothetical shoe retailer stock administration system. As with every new .NET utility, we’ll have to create an answer after which add a undertaking to it. We are able to leverage the .NET SDK CLI instruments to scaffold our new answer:
mkdir Shoestore && cd Shoestore
dotnet new sln
Subsequent, create an ASP.NET undertaking containing an express fundamental class for simplicity’s sake, as this undertaking construction is most acquainted to ASP.NET builders. Let’s create our undertaking utilizing the MVC sample:
mkdir Shoestore.mvc && cd Shoestore.mvc
dotnet new mvc --use-program-main=true
Subsequent, add the undertaking into the answer:
# Go to the foundation of the answer
cd ..
dotnet sln add Shoestore.mvc/
We now have a default answer and its contained ASP.NET undertaking. Earlier than continuing, be certain that every thing builds:
cd Shoestore.mvc/
dotnet restore
dotnet construct
Good growth apply encourages placing key providers and the appliance runtime into Docker containers for improved deployment and portability. Subsequently, let’s create a easy Docker container to help our utility.
Utility Portability
Docker photographs sometimes reference one other father or mother Docker picture as an accepted place to begin for important necessities like OS and fundamental options, together with databases. Following this Docker finest apply, create each a Dockerfile and a Docker Compose file for correct service configuration whereas referencing Microsoft-published father or mother photographs. We’ll use Docker phases to maintain our picture small. Phases enable us to make use of the .NET SDK whereas constructing our utility in order that the ASP.NET runtime is required solely whereas our utility runs.
Create the Shoestore.mvc
Dockerfile with the next contents:
# ShoestoreShoestore.mvcDockerfile
# Construct stage
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS construct
WORKDIR /shoestore
COPY Shoestore.mvc/*.csproj ./
# Restore undertaking packages
RUN dotnet restore
COPY Shoestore.mvc/* ./
# Create a launch construct
RUN dotnet construct -c Launch -o /app/construct
# Run the appliance and make it accessible on port 80
FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app
EXPOSE 80
# Belongings and views
COPY Shoestore.mvc/Views ./Views
COPY Shoestore.mvc/wwwroot ./wwwroot
COPY --from=construct /app/construct ./
ENTRYPOINT [ "dotnet", "Shoestore.mvc.dll" ]
Subsequent, we’ll create the docker-compose.yml
file in our answer’s root listing. Initially, it’ll solely include a reference to our utility service’s .Dockerfile
:
# Shoestore/docker-compose.yml
model: "3.9"
providers:
net:
construct:
context: .
dockerfile: Shoestore.mvc/Dockerfile
ports:
- "8080:80"
Let’s additionally configure our surroundings with a .dockerignore file to make sure that solely the construct artifacts are copied to our picture.
With our utility service now stubbed in and its execution atmosphere able to run, we have to create our database service and join it to our Docker configuration.
The Database Service
Including the Microsoft SQL Server to our Docker configuration is simple, particularly since we’re utilizing a Microsoft-provided Docker picture with out altering it. Add the next configuration block to the underside of the docker-compose.yml
file to configure the database:
db:
picture: "mcr.microsoft.com/mssql/server"
atmosphere:
SA_PASSWORD: "custom_password_123"
ACCEPT_EULA: "Y"
ports:
- "1433:1433"
Right here, ACCEPT_EULA
prevents the set up from halting, and our ports
setting lets the default SQL Server port go via with out translation. With that, our Compose file consists of each our utility service and database.
Earlier than customizing the appliance code, let’s confirm that our Docker atmosphere works:
# From the foundation of the answer
docker compose up --build
Assuming no errors seem throughout startup, our incomplete pattern utility ought to be accessible via an online browser on the native deal with http://localhost:8080
.
Now we get to give attention to the enjoyable half: customizing the appliance code and making certain that the appliance knowledge persists within the Microsoft SQL Server database. We’ll use each the Entity Framework (EF) and .NET SDK instruments to attach the appliance to the database and scaffold the appliance’s mannequin, view, controller, and EF-required configuration.
Earlier than we are able to specify the instruments we’d like, we should create a tool-manifest
file:
# From the foundation of the answer
dotnet new tool-manifest
Add the EF and SDK instruments to this file with these easy instructions:
dotnet device set up dotnet-ef
dotnet device set up dotnet-aspnet-codegenerator
To confirm the correct set up of those instruments, run dotnet ef
. If a unicorn seems, they’re put in appropriately. Subsequent, run dotnet aspnet-codegenerator
to check the ASP.NET instruments; the output ought to be a normal CLI utilization block.
Now we are able to use these instruments to create our utility.
MVC: Mannequin
The primary job in constructing our utility is creating the mannequin. Since this mannequin will likely be added to the database later, we’ll embrace the MS SQL Server and EF packages in our undertaking:
cd Shoestore.mvc/
dotnet add package deal Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
dotnet add package deal Microsoft.EntityFrameworkCore.SqlServer
dotnet add package deal Microsoft.EntityFrameworkCore.Instruments
dotnet restore
Subsequent, create an EF database context object that determines which fashions are added to the database, and permits our code to simply entry and question that knowledge from the database.
Create a Knowledge
listing to deal with the EF-specific code and create the ApplicationDBContext.cs
file with the next contents:
// Shoestore/Shoestore.mvc/Knowledge/ApplicationDBContext.cs
utilizing Microsoft.EntityFrameworkCore;
namespace Shoestore.mvc.Knowledge;
public class ApplicationDBContext : DbContext
{
public ApplicationDBContext(DbContextOptions<ApplicationDBContext> choices):base(choices){}
}
Subsequent, configure the database connection string, which should match the credentials we configured in our Dockerfile
. Set the contents of Shoestore/Shoestore.mvc/appsettings.json
to the next:
{
"Logging": {
"LogLevel": {
"Default": "Info",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"Shoestore": "Server=db;Database=grasp;Consumer=sa;Password=custom_password_123;"
}
}
With the database connection string configured and database context coded, we’re able to code our utility’s Major
operate. We’ll embrace database exception dealing with to simplify system debugging. Moreover, as a result of a .NET bug within the generated code causes the Docker container to serve our views incorrectly, we’ll want so as to add particular code to our view service configuration. It will explicitly set the file paths to our view location in our Docker picture:
utilizing Microsoft.EntityFrameworkCore;
utilizing Shoestore.mvc.Knowledge;
namespace Shoestore.mvc;
// ...
public static void Major(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Affiliate our EF database context and configure it with our connection string
var connectionString = builder.Configuration.GetConnectionString("Shoestore");
builder.Companies.AddDbContext<ApplicationDBContext>(
choices => choices.UseSqlServer(connectionString));
// Middleware to catch unhandled exceptions and show a stack hint
builder.Companies.AddDatabaseDeveloperPageExceptionFilter();
// Add providers to the container.
// ASP.NET has a identified challenge the place the ultimate constructed app does not know the place the view
// information are (within the Docker container).
// The repair is to particularly add view places.
builder.Companies
.AddControllersWithViews()
.AddRazorOptions(choices => {
choices.ViewLocationFormats.Add("/{1}/{0}.cshtml");
choices.ViewLocationFormats.Add("/Shared/{0}.cshtml");
});
Skip right down to the IsDevelopment
if
assertion throughout the identical file so as to add a database migration endpoint to our system when it’s in growth mode. Add an else
assertion with the next code:
// Configure the HTTP request pipeline.
if (!app.Setting.IsDevelopment())
{
// Go away the contents of the if block alone. These are hidden for readability.
} else {
app.UseMigrationsEndPoint();
}
Subsequent, run a fast check to make sure the brand new packages and the supply code edits compile appropriately:
// Go to mvc listing
cd Shoestore.mvc
dotnet restore
dotnet construct
Now, let’s populate the mannequin with our required fields by creating the Shoestore.mvcModelsShoe.cs
file:
namespace Shoestore.mvc.Fashions;
public class Shoe {
public int ID { get; set; }
public string? Identify { get; set; }
public int? Value { get; set; }
public DateTime CreatedDate { get; set; }
}
EF generates SQL primarily based on the related mannequin, its context file, and any EF code in our utility. SQL outcomes are then translated and returned to our code, as wanted. If we add our Shoe
mannequin to our database context, EF will know methods to translate between MS SQL Server and our utility. Let’s do that within the database context file, Shoestore/Shoestore.mvc/Knowledge/ApplicationDBContext.cs
:
utilizing Microsoft.EntityFrameworkCore;
utilizing Shoestore.mvc.Fashions;
namespace Shoestore.mvc.Knowledge;
public class ApplicationDBContext : DbContext
{
public ApplicationDBContext(DbContextOptions<ApplicationDBContext> choices) : base(choices) { }
personal DbSet<Shoe>? _shoe { get; set; }
public DbSet<Shoe> Shoe {
set => _shoe = worth;
get => _shoe ?? throw new InvalidOperationException("Uninitialized property" + nameof(Shoe));
}
}
Lastly, we’ll use a database migration file to get our mannequin into the database. The EF device creates a migration file particular to MS SQL Server primarily based on the database context and its related mannequin (i.e., Shoe
):
cd Shoestore.mvc/
dotnet ef migrations add InitialCreate
Let’s maintain off on working our migration till we’ve a controller and think about in place.
MVC: Controller and View
We’ll create our controller utilizing the ASP.NET code technology device. This device could be very highly effective however requires particular helper lessons. Use the Design
model packages for the fundamental controller construction and its EF integration. Let’s add these packages:
cd Shoestore.mvc
dotnet add package deal Microsoft.VisualStudio.Net.CodeGeneration.Design &&
dotnet add package deal Microsoft.EntityFrameworkCore.Design &&
dotnet restore
Now, creating our default controller is so simple as invoking the next command:
cd Shoestore.mvc
dotnet dotnet-aspnet-codegenerator controller
-name ShoesController
-m Shoe
-dc ApplicationDBContext
--relativeFolderPath Controllers
--useDefaultLayout
--referenceScriptLibraries
When the code generator creates our controller, it additionally creates a easy view for that controller. With our MVC foundations full, we’re able to get every thing working.
Migration and Utility Check
EF migrations are normally a easy affair, however when Docker is concerned, the method turns into extra complicated. Within the subsequent article in our collection, we’ll discover the splendidly twisty path to creating these migrations work in our Docker answer, however for now, we simply need our migration to run.
All configuration and migration information are included in our repository. Let’s clone the total undertaking to our native machine and carry out the migration:
git clone https://github.com/theZetrax/dot-net-on-linux.git
cd ./dot-net-on-linux
docker composer up
The docker composer
operation builds our utility, runs the migration, and launches our ASP.NET utility with the .NET runtime. To entry the working answer, go to http://localhost:8080/Sneakers
.
Though our utility interface is straightforward, it demonstrates performance via all tiers, from the view right down to the database.
.NET on Linux Simply Works
See the full repository for an summary of our answer. The following article will cowl our migration intimately, in addition to suggestions and tips to make our Docker photographs lean.
.NET on Linux is greater than only a pipe dream: It’s a viable language, runtime, and OS mixture. Many builders raised on Visible Studio could not have expertise utilizing .NET CLI to its fullest, however these instruments are efficient and highly effective.
Additional Studying on the Toptal Engineering Weblog:
The Toptal Engineering Weblog extends its gratitude to Henok Tsegaye for reviewing the code samples introduced on this article.