-
Book Overview & Buying
-
Table Of Contents
-
Feedback & Rating

Tools and Skills for .NET 8
By :

We need a database and some projects that we can use throughout this book. To make it reasonably realistic, we need multiple projects that use common features like a SQL Server database, class libraries, unit tests, and so on.
We will define an entity data model as a pair of reusable class libraries. One part of the pair will define the entities like Product
and Customer
. The second part of the pair will define the tables in the database, the default configuration for how to connect to the database, and use the Fluent API to configure additional options for the model.
We will create three projects:
Category
and Product
named Northwind.EntityModels
Northwind.DataContext
Northwind.Tests
It would be useful to have a sample database that has a medium complexity and a decent number of sample records. Microsoft offers several sample databases, most of which are too complex for our needs, so instead, we will use a database that was first created in the early 1990s known as Northwind.
Let’s take a minute to look at a diagram of the Northwind database and its eight most important tables. You can use the diagram in Figure 1.2 to refer to as we write code and queries throughout this book:
Figure 1.2: The Northwind database tables and relationships
Note that:
Categories
and Products
is one-to-many, meaning each category can have zero, one, or more products.ReportsTo
field is null
), and a photo stored as a byte array in JPEG format. The table has a one-to-many relationship to itself because one employee can manage many other employees.Microsoft offers various editions of its popular and capable SQL Server product for Windows, Linux, and Docker containers.
If you have Windows, then you can use a free version that runs standalone, known as SQL Server Developer Edition. You can also use the Express edition or the free SQL Server LocalDB edition that can be installed with Visual Studio. To install SQL Server locally on Windows, please see the online instructions at the following link: https://github.com/markjprice/tools-skills-net8/blob/main/docs/sql-server/README.md. If you prefer to install SQL Server locally on Linux, then you will find instructions at the following link: https://learn.microsoft.com/en-us/sql/linux/sql-server-linux-setup.
If you do not have a Windows computer or if you want to use a cross-platform database system, then please see the online-only section, Installing Azure SQL Edge in Docker, found at the following link: https://github.com/markjprice/tools-skills-net8/blob/main/docs/sql-server/edge.md.
You’ll need to have set up SQL Server, run the SQL script to create the Northwind database and confirm that you can connect to the database and view the rows in its tables like Products
and Categories
before continuing with the project. The following two subsections provide detailed steps to help you do so using either a local SQL Server or SQL Edge in Docker. You can skip this if you already have this set up.
To run a SQL script to create the Northwind sample database for a local SQL Server:
/scripts/sql-scripts/Northwind4SQLServer.sql
into a working folder..\tools-skills-net8
.Northwind4SQLServer.sql
file and then click the Open button.To run a database script to create the Northwind sample database for SQL Edge in Docker:
Northwind4AzureSQLedge.sql
file.tcp:127.0.0.1,1433
sa
user already created and you had to give it a strong password when you ran the container. We chose the password s3cret-Ninja
.master
or leave blank. We will create the Northwind database using a SQL script.Good Practice
You should create a separate class library project for your entity data models. This allows easier sharing between backend web servers and frontend desktop, mobile, and Blazor clients.
You will now create the entity models using the dotnet-ef
tool:
classlib
Northwind.EntityModels
Chapter01
Northwind.EntityModels
project, treat warnings as errors, and add package references for the SQL Server database provider and EF Core design-time support, as shown highlighted in the following markup:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference
Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.6" />
<PackageReference
Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
You can check the most recent package versions at the following links: https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.SqlServer and https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Design.
If you are unfamiliar with how packages like Microsoft.EntityFrameworkCore.Design
can manage their assets, then you can learn more at the following link: https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets.
Good Practice
By default, compiler warnings may appear if there are potential problems with your code when you first build a project, but they do not prevent compilation and they are hidden if you rebuild. Warnings are given for a reason, so ignoring warnings encourages poor development practices. I recommend that you force yourself to fix warnings by enabling the option to treat warnings as errors.
Class1.cs
file.Northwind.EntityModels
project.Northwind.EntityModels
folder.dotnet-ef
tool, then install the latest version, as shown in the following command:
dotnet tool install --global dotnet-ef
dotnet tool update --global dotnet-ef
The next step assumes a database connection string for a local SQL Server authenticated with Windows Integrated Security. Modify it for Azure SQL Edge with a user ID and password if necessary.
dotnet ef dbcontext scaffold "Data Source=.;Initial
atalog=Northwind;Integrated Security=true;TrustServerCertificate=True;" Microsoft.EntityFrameworkCore.SqlServer --namespace Northwind.EntityModels --data-annotations
Note the following:
dbcontext scaffold
."Data Source=.;Initial Catalog=Northwind;Integrated Security=true;TrustServerCertificate=True;"
Microsoft.EntityFrameworkCore.SqlServer
--namespace Northwind.EntityModels
--data-annotations
AlphabeticalListOfProduct.cs
to Territory.cs
.NorthwindContext.cs
file, import the namespace for working with ADO.NET types, as shown in the following code:
using Microsoft.Data.SqlClient; // To use SqlConnectionStringBuilder.
OnConfiguring
method to dynamically set the connection string and set any sensitive parameters using environment variables, as shown in the following code:
protected override void OnConfiguring(
DbContextOptionsBuilder optionsBuilder)
{
// If not already configured by a client project. For example,
// a client project could use AddNorthwindContext to override
// the database connection string.
if (!optionsBuilder.IsConfigured)
{
SqlConnectionStringBuilder builder = new();
builder.DataSource = ".";
builder.InitialCatalog = "Northwind";
builder.TrustServerCertificate = true;
builder.MultipleActiveResultSets = true;
// If using Azure SQL Edge.
// builder.DataSource = "tcp:127.0.0.1,1433";
// Because we want to fail faster. Default is 15 seconds.
builder.ConnectTimeout = 3;
// If using Windows Integrated authentication.
builder.IntegratedSecurity = true;
// If using SQL Server authentication.
// builder.UserID = Environment.GetEnvironmentVariable("MY_SQL_USR");
// builder.Password = Environment.GetEnvironmentVariable("MY_SQL_PWD");
optionsBuilder.UseSqlServer(builder.ConnectionString);
}
}
Customer.cs
, the dotnet-ef
tool correctly identified that the CustomerId
column is the primary key and it is limited to a maximum of five characters, but we also want the values to always be uppercase. So, add a regular expression to validate its primary key value to only allow uppercase Western characters, as shown highlighted in the following code:
[Key]
[StringLength(5)]
[RegularExpression("[A-Z]{5}")]
public string CustomerId { get; set; } = null!;
Next, you will move the context model that represents the database to a separate class library:
classlib
Northwind.DataContext
Chapter01
DataContext
project, add a project reference to the EntityModels
project, and add a package reference to the EF Core data provider for SQL Server, as shown in the following markup:
<ItemGroup>
<PackageReference
Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.6" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include=
"..\Northwind.EntityModels\Northwind.EntityModels.csproj" />
</ItemGroup>
You can try out previews of EF Core 9 by specifying version 9.0-*
. The target framework for your project should continue to use net8.0
. By using a wildcard, you will automatically download the latest monthly preview when you restore the packages for the project. Once the EF Core 9 GA version is released in November 2024, change the package version to 9.0.0
or later. After February 2025, you will be able to do similar with EF Core 10 (use a package version of 10.0-*
) but that will likely require a project targeting net10.0
so you will have to install a preview version of .NET 10 SDK as well.
Northwind.DataContext
project, delete the Class1.cs
file.Northwind.DataContext
project.NorthwindContext.cs
file from the Northwind.EntityModels
project/folder to the Northwind.DataContext
project/folder.Northwind.DataContext
project, add a class named NorthwindContextExtensions.cs
, and modify its contents to define an extension method that adds the Northwind database context to a collection of dependency services, as shown in the following code:
using Microsoft.Data.SqlClient; // SqlConnectionStringBuilder
using Microsoft.EntityFrameworkCore; // UseSqlServer
using Microsoft.Extensions.DependencyInjection; // IServiceCollection
namespace Northwind.EntityModels;
public static class NorthwindContextExtensions
{
/// <summary>
/// Adds NorthwindContext to the specified IServiceCollection. Uses the SqlServer database provider.
/// </summary>
/// <param name="services">The service collection.</param>
/// <param name="connectionString">Set to override the default.</param>
/// <returns>An IServiceCollection that can be used to add more services.</returns>
public static IServiceCollection AddNorthwindContext(
this IServiceCollection services,
string? connectionString = null)
{
if (connectionString == null)
{
SqlConnectionStringBuilder builder = new();
builder.DataSource = ".";
builder.InitialCatalog = "Northwind";
builder.TrustServerCertificate = true;
builder.MultipleActiveResultSets = true;
// If using Azure SQL Edge.
// builder.DataSource = "tcp:127.0.0.1,1433";
// Because we want to fail fast. Default is 15 seconds.
builder.ConnectTimeout = 3;
// If using Windows Integrated authentication.
builder.IntegratedSecurity = true;
// If using SQL Server authentication.
// builder.UserID = Environment.GetEnvironmentVariable("MY_SQL_USR");
// builder.Password = Environment.GetEnvironmentVariable("MY_SQL_PWD");
connectionString = builder.ConnectionString;
}
services.AddDbContext<NorthwindContext>(options =>
{
options.UseSqlServer(connectionString);
// Log to console when executing EF Core commands.
options.LogTo(Console.WriteLine,
new[] { Microsoft.EntityFrameworkCore
.Diagnostics.RelationalEventId.CommandExecuting });
},
// Register with a transient lifetime to avoid concurrency
// issues with Blazor Server projects.
contextLifetime: ServiceLifetime.Transient,
optionsLifetime: ServiceLifetime.Transient);
return services;
}
}
Good Practice
We have provided an optional argument for the AddNorthwindContext
method so that we can override the SQL Server database connection string. This will allow us more flexibility, for example, to load these values from a configuration file.
Since we will not be creating a client project in this chapter that uses the EF Core model, we should create a test project to make sure the database context and entity models integrate correctly:
xunit
project named Northwind.Tests
to the Chapter01
solution.Northwind.Tests.csproj
, modify the configuration to treat warnings as errors and add an item group with a project reference to the Northwind.DataContext
project, as shown in the following markup:
<ItemGroup>
<ProjectReference Include=
"..\Northwind.DataContext\Northwind.DataContext.csproj" />
</ItemGroup>
Warning!
The path to the project reference should not have a line break in your project file.
Northwind.Tests
project to build and restore project dependencies.UnitTest1.cs
to NorthwindEntityModelsTests.cs
(Visual Studio prompts you to rename the class when you rename the file).NorthwindEntityModelsTests.cs
, if you are using Code, then manually rename the class to NorthwindEntityModelsTests
.NorthwindEntityModelsTests.cs
, modify the class to import the Northwind.EntityModels
namespace and have some test methods for ensuring that the context class can connect, the provider is SQL Server, and the first product is named Chai
, as shown in the following code:
using Northwind.EntityModels; // To use NorthwindContext and Product.
namespace Northwind.Tests;
public class NorthwindEntityModelsTests
{
[Fact]
public void CanConnectIsTrue()
{
using (NorthwindContext db = new()) // arrange
{
bool canConnect = db.Database.CanConnect(); // act
Assert.True(canConnect); // assert
}
}
[Fact]
public void ProviderIsSqlServer()
{
using (NorthwindContext db = new())
{
string? provider = db.Database.ProviderName;
Assert.Equal("Microsoft.EntityFrameworkCore.SqlServer", provider);
}
}
[Fact]
public void ProductId1IsChai()
{
using (NorthwindContext db = new())
{
Product? product1 = db?.Products?.Single(p => p.ProductId == 1);
Assert.Equal("Chai", product1?.ProductName);
}
}
}
Now we are ready to run the tests and see the results using either Visual Studio or Code.
Using Visual Studio:
Northwind.Tests
project, and then select Run Tests.Figure 1.3: All the tests passed
Using Code:
Northwind.Tests
project’s TERMINAL window, run the tests, as shown in the following command:
dotnet test
If you are using C# Dev Kit, then you can also build the test project and then run the tests from the Testing section in the Primary Side Bar.
Change the font size
Change margin width
Change background colour