- Learn how to use entities
- Learn how to define a persistence layer
- Learn how to use Entity Framework
- Learn how to work with migrations
- Learn how to work with seeds
In this exercise we are going to add a database to our e-commerce website.
This repository contains the solution code from the previous chapter. The classes OrderItem
and CartItem
were removed because the project was a little over engineered. These classes only existed for easier comparison of these lines but made the database configuration more difficult.
The Order
class also got an extra property for the Customer
.
- Add the following connection string to the
appsettings.Development.json
of theServer
project
"ConnectionStrings": {
"SportStore": "Server=(localdb)\\mssqllocaldb;Database=SportStore;Trusted_Connection=True;"
}
- Create a new Class Library in the
src
folder calledPersistence
and add it to the solution, remove the dummyClass1.cs
- Add the following dependencies to the
Persistence
project:- Microsoft.EntityFrameworkCore.Design
- Microsoft.EntityFrameworkCore.SqlServer
- Add the following dependencies to the
Server
project:- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tools
- Add a reference from the
Persistence
to theDomain
project. - Add a reference from the
Services
to thePersistence
project.- Since the
Server
project uses theServices
project, we get a child dependency on thePersistence
project
- Since the
- Create a
DbContext
namedSportStoreDbContext
in aData
folder in thePersistence
project- This class only contains a constructor which calls the base class' constructor
- Initialize a connection in the
StartUp
of theServer
- Add a private default constructor for every class in the
Domain
. Note not every domain class should be persisted, how do you know? - Read through this documentation: Persist value objects as owned entity types and make every
ValueObject
an owned entity type.- Create seperate
IEntityTypeConfiguration
subclasses per class that needs modification, see the documentation for more info. - For readonly properties, you need to specify the property in the model builder or Entity Framework won't add it to the database. An alternative is to add a private setter but this is bad practice because it's not obvious why the setter is private and it's not clear at first sight which properties are stored in the database.
- Set a precision of 12 and a scale of 10 for the
Value
of classMoney
- Create seperate
- Add a
DbSet
for each entity that can be fetched - Create a migration name InitialCreate which contains all data to create the empty database
- If the CLI tells you that the
ef
command does not exist, install the tool:dotnet tool install --global dotnet-ef --version 5.0.12
- If the CLI tells you that the
- Create a class named
SportStoreDataInitializer
and paste the following code:
public class SportStoreDataInitializer
{
private readonly SportStoreDbContext _dbContext;
public SportStoreDataInitializer(SportStoreDbContext dbContext)
{
_dbContext = dbContext;
}
public void SeedData()
{
_dbContext.Database.EnsureDeleted();
if (_dbContext.Database.EnsureCreated())
{
SeedProducts();
}
}
private void SeedProducts()
{
var products = new ProductFaker()
.RuleFor(p => p.Id, () => 0) // Remove the id, database column is auto generated
.RuleFor(p => p.Category, new CategoryFaker().RuleFor(c => c.Id, 0))
.Generate(100);
_dbContext.Products.AddRange(products);
_dbContext.SaveChanges();
}
}
- Make this class a scoped service and inject an instance in the
Configure
method, finally call theSeed
method - Check if Entity Framework mapped every entity correctly. If not, create or update the
IEntityTypeConfiguration
for that specific entity, create a new migration, make sure the server executed the new migration and check again. Repeat this step until the database model is correct.- The only optional fields are
Description
(Product
) andDeliveryDate
(Order
) - The
Total
inOrder
should not be persisted
- The only optional fields are
- Implement a new
ProductService
which interacts with the database - Use this new service in the
Server
(instead of theFakeProductService
) - Make sure everything works
A possible solution can be found here.