Build Layered Architecture with ASP.NET Core, Entity Framework Core in a Real-Word Example
This article explains aspnetrun core realworld repository of github. This series of articles continues as per difficulty levels. This article intended for the intermediate of asp.net core. You can follow the other articles at the end of the page.
Introduction
In this article we will show how to build layered web application on ASP.NET Core Web application using Entity Framework and Razor Pages.
By the end of the article, we will have a NLayer Hexagonal architecture (Core, Application, Infrastructure and Presentation Layers) web application which implemented e-commerce pages over Product,Category and Contact entities.
Take a look at the final application.
You’ll learn how to:
- Create Layered Application with ASP.NET Core Web Application Project
- Work with a Database using Entity Framework Core
- E&E e-commerce theme with Bootstrap 4 Implementation
- Full development of e-commerce use cases of Northwind database
- Business rule check, domain policy apply and validations
- ASP.NET Core Identity Scaffolding and Identity Configuration
- Authorization for Pages
- ASP.NET Core Razor Tools — View Components, partial Views, Tag Helpers, Model Bindings and Validations, Razor Sections etc..
- Configuration management
- Custom paging implementation
At the end, you’ll have a working web application running on your local machine.
Source Code
Get the Source Code from AspnetRunCore RealWorld Github — Clone or fork this repository, if you like don’t forget the star :) If you find or ask anything you can directly open issue on repository.
Prerequisites
- Install the .NET Core 3.x or above SDK
- Install Visual Studio 2019 v16.x or above
- Microsoft Sql Server Express (its comes with Visual Studio)
Background
You can follow the previous article which base application of this implementation of this Basic repository in a real-world example.
Check for the previous article which explained base of this repository.
Based on that we were created a layered infrastructure of crud operations on asp.net core web application from previous post. Over this repository, we are going to create a real-world e-commerce web application with using ASP.Net Core best practices.
Start the Web Application
We are going to build our application on top of the core crud implementation of this repository. So lets download — clone or fork this repository and open the solution.
At this point, we have a CRUD implemented Pages project. Run the project (Crtl + F5) to see everything is OK.
Rename Your Application
At this point, you can rename your application name by changing Solution and Project Names.
Select Solution file, Right Click -> Rename -> Your New Project Name..
Select Project file, Right Click -> Rename -> Your New Project Name..
Project Structure
Examine the project files
Here’s an overview of the main project folders and files that you will work with in our operations.
Entities folder
Under the Core layer, check Entity classes that provide all tables for e-commerce domain. This folder include model classes which we use all layers and manage this entities when Entity Framework Core persist on database and other database operations. We will use this folder for the new Entity definitions.
Data folder
Under the Infrastructure layer, check AspnetRunContext.cs class which basically include DbSet definitions for EF.Core. This folder represent our Data Layer and includes Entity Framework Core implementation. Also include Seed class in order to load data when application start-up. We will add our Entities as a DBSet into DBContext object.
Repositories folder
Under the Infrastructure layer, check Order-Product etc.. repositories that has signature of business operations. This folder represent our Business Layer and includes Repository classes which wrappers of Entity Framework Core DbContext object. Since this is the one solution, we decided to named folder as Repository due to implement both wrapping EF.Core and implement business related logic’s in one class.
Pages folder
Under the Web layer, check Pages which basically implemented e-commerce application pages with E&E web template. This folder represent our Presentation Layer and includes Razor pages and supporting files. We will extend existing pages and add new Areas to implement e-commerce web application.
Starting Our Project
This project will be basic implementation of e-commerce web application. So we should define our basic use case analysis.
Its really important to understand what you will develop and its crucial that defining your use cases. So this run-aspnetcore is a base repository but we are implementing this basement into run-aspnetcore-realworld repository which basically create a e-commerce web application.
This part mostly referring the ZAN KAVTASKIN — Applied Domain-Driven Design (DDD), Part 0 — Requirements and Modelling
Lets analysis e-commerce domain in order to provide our development roadmap.
Requirements and Modelling
- Identify User Stories
- Identify the Nouns in the user stories
- Identify the Verbs in the user stories
- Put together object interaction diagram
- Put together object responsibilities diagram
- Put together class digram UML showing only interesting interactions
So here are made up user stories:
- As a customer I want to list products
- As a customer I want to be able to filter products as per brand and categories
- As a customer I want to see the supplier of product in the product detail screen with all characteristics of product
- As a customer I want to be able to put products that I want to purchase in to the shopping cart so that I can check out quickly later on
- As a customer I want to see the total cost all for all of the items that are in my cart so that I see if I can afford to buy everything
- As a customer I want to see the total cost of each item in the shopping cart so that I can re-check the price for items
- As a customer I want to be able to specify the address of where all of the products are going to be sent to
- As a customer I want to be able to add a note to the delivery address so that I can provide special instructions to the postman
- As a customer I want to be able to specify my credit card information during check out so that I can pay for the items
- As a customer I want system to tell me how many items are in stock so that I know how many items I can purchase
- As a customer I want to receive order confirmation email with order number so that I have proof of purchase
- As a customer I want to list my old orders and order items history
- As a customer I want to login the system as a user and the system should remember my shopping cart items
Now I am going extract nouns and verbs from the stories above. I am looking for the nouns that will become my main objects and not the attributes.
Nouns:
- Customer
- Order
- Order Details
- Product
- Shopping Cart
- Shopping Cart Items
- Supplier
- User
- Address
- Brand
- Category
Verbs:
- List products applying to paging
- Filter products by brand, category and supplier
- See product all information in the details screen
- Put products in to the shopping cart
- See total cost for all of the items
- See total cost for each item
- Checkout order with purchase steps
- Specify delivery address
- Specify delivery note for delivery address
- Specify credit card information
- Pay for the items
- Tell me how many items are in stock
- Receive order confirmation email
- List the order and details history
- Login the system and remember the shopping cart items
By using above nouns and verbs we can put together a diagram such as this:
Once we have object interaction diagram we can start thinking about object responsibilities. One of the most common mistakes is to push responsibilities on to the actor object i.e. Customer. We need to remember that objects must take care of themselves and objects need to be closed for direct communication and that you need go through the functions to communicate with them.
So let’s follow above approach and assign responsibilities:
Now that we have object interaction and responsibilities diagram in place we can start thinking about lower level UML class diagram.
Methods, class names, dependencies, interfaces and compositions should be defined as per conceptual responsibility diagram. And we should define methods the main objects. For example ShoppingCart.cs should have AddItemToCart, GetItem and so on methods.
Business Rules — Domain Policies :
When you are implementing the domain related functions, there should be some rules to be apply. So after defining your class and methods also we need to clarify exceptional cases with defining business rules, domain policies;
- Shopping cart should not have more then 5 item.
- Shopping cart item quantity should not have more then 10.
- All orders must have a non-empty delivery address.
- The day of delivery cannot be Sunday.
So after this analysis we can start coding and develop our model to code. Lets continue..
Where We Start Coding
After defining verbs of project, its good to start coding from Application layer. Because in Application Layer we are building application use cases and its good starting point of over the AspNetRun template codes. For example lets check for below verbs;
Verbs:
- Filter products by brand, category and supplier
- Put products in to the shopping cart
So for the first verb directly we can add related methods into IProductAppService.cs class in below way. If there is no application layer interface we should add this interface into Application-Interfaces folder.
public interface IProductAppService
{
....
Task<IEnumerable<ProductDto>> GetProductByBrand(int brandId);
Task<IEnumerable<ProductDto>> GetProductBySupplier(int supplierId);
....
}
So by this way, we will continue to implement this interface in Application-Services folder.
public class ProductAppService : IProductAppService
{
public async Task<IEnumerable<ProductDto>> GetProductByCategory(int categoryId)
{
var productList = await _productRepository.GetProductByCategoryAsync(categoryId);
var mapped = ObjectMapper.Mapper.Map<IEnumerable<ProductDto>>(productList);
return mapped;
} public Task<IEnumerable<ProductDto>> GetProductByBrand(int brandId)
{
// TODOX : develop
throw new NotImplementedException();
} public Task<IEnumerable<ProductDto>> GetProductBySupplier(int supplierId)
{
// TODOX : develop
throw new NotImplementedException();
}
}
When we try to implement this methods, figure out that we should also create _productRepository methods as expected way and so on. Developments of AspNetRun web application should continue like that. After finished back-end side, we should call this application methods from Web projects. But you got the idea of starting point of developments from business requirements with making analysis.
For example the second verb classes should become as below;
- Verb : Put products in to the shopping cart
- Classes :
- IShoppingCartAppService.cs — AddToShoppingCart method in Application layer.
- Implementation of this method in ShoppingCartAppService.cs — AddToShoppingCart in Application layer.
- Preparing shopping cart item insert method in IShoppingCartRepository in Core layer.
- Implementation of this method in ShoppingCartRepository.cs in Infrastructure layer.
This part mostly refferred the ZAN KAVTASKIN — Applied Domain-Driven Design (DDD), Part 0 — Requirements and Modelling
Set Entities
According to above analysis, lets develop entities. Go to Entities folder into your project. This will be the tables of your project. In this post, we will use the Code First Approach of the Entity Framework Core. That’s why we write the entity classes at first.
Add or Edit Product, Category,Cart,CartItem,Compare,Contact,List, Order,OrderItem,Compare, Wishlist etc.. class into Entities folder.
public class Product
{
public int Id { get; set; }
[Required, StringLength(80)]
public string Name { get; set; }
public string Summary { get; set; }
public string Description { get; set; }
public string ImageFile { get; set; }
public int UnitPrice { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}....
Set Data Layer
In order to manage these entities, we should create a data structure. To work with a database, we will use this class with the Entity Framework Core. The Entity Framework Core is an object-relational mapping (ORM) framework that provide data access over the DbContext class. Entity classes are not dependent on Entity Framework Core.
Ensure that every Entity classes in this DataContext class as a DbSet generic.
public class AspnetRunContext : DbContext
{
public AspnetRunContext(DbContextOptions<AspnetRunContext> options): base(options)
{}
public DbSet<Blog> Blogs { get; set; }
public DbSet<Cart> Carts { get; set; }
public DbSet<CartItem> CartItems { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Compare> Compares { get; set; }
public DbSet<Contact> Contacts { get; set; }
public DbSet<List> Lists { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<OrderItem> OrderItems { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<Review> Reviews { get; set; }
public DbSet<Specification> Specifications { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Wishlist> Wishlists { get; set; }
}
At this point, You can rename your Context Class here.
Click the AspnetRunContext class definition, Ctrl + R + R or Right Click-> Rename -> Your New Context Name..
Configure Entity Framework DbContext into Asp.Net Core
Entity Framework Core DB context object should registered in DI when starting the application. Ensure that DbContext object into ConfigureServices method is configured properly.
public void ConfigureServices(IserviceCollection services)
{
#region Database Connection
//// use in-memory database
//services.AddDbContext<AspnetRunContext>(c => c.UseInMemoryDatabase(“AspnetRunConnection”));// add real database ependency
services.AddDbContext<AspnetRunContext>(c => c.UseSqlServer(Configuration.GetConnectionString(“AspnetRunConnection”)));
#endregion
services.AddRazorPages();
}
Update Connection String
If you use the real database, its required to set-up connection string information. In Asp.Net Core this configuration stored appsettings.json file;
“ConnectionStrings”: {
“AspnetRunConnection”: “Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=AspnetRunBasic;”
}
At this point, You can rename your Database name.
In appsettings.json file change the Initial Catalog=AspnetRunRealWorld name.
Create Database using EF.Core Migrations
After definition of connection string, its ready to create database. In order to create database with Entity Framework Core, we should use EF.Core Migrations feature.
Before that you should delete Migration folder in your project. Because we should create new fresh initial migration class for our project.
Open Tools -> NuGet Package Manager > Package Manager Console(PMC) and run the following command in the package manager console:
add-migration Initial
This command generates code to create the initial database schema which is based on the model specified in the YourNewContext class. The Initial argument is the migration name and any name can be used. After running the command, Entity Framework Core will create Migrations folder in order to track changes on the DbContext entities.
To commit these changes into the database, run the following command in the package manager console:
update-database
This command runs the Up method in the Migrations/{time-stamp}_Initial.cs file, which creates the database.
Now, you can check the database created from Sql Server Object Explorer. Check the newly created database under the following path: Open View -> Sql Server Object Explorer.
You will see the your project tables under the your new database name.
Register Classes into ASP.NET Dependency Injection Engine
You should register this classes into ASP.NET Built-in Dependency Injection engine.That means we should recognize these classes into asp.net core.
Open Startup.cs -> Go To Method ConfigureAspnetRunServices -> put your dependencies;
private void ConfigureAspnetRunServices(IserviceCollection services){
…..#region project services
// add repository ependency
services.AddScoped<IproductRepository, ProductRepository>();
services.AddScoped<IcontactRepository, ContactRepository>();
#endregion
}
Create Presentation Layer
We were created a Default Web Application template for ASP.NET Core project. So that means we have a Razor Pages template into Pages folder.
ASP.NET Core Identity
According to requirements, the Login Page and Identity Management should be handled. For this kind of requirements, ASP.NET has scaffolding feature performs for the whole Identity operations.
Before we start in order to generate Login Pages, firstly we have to change Entity Framework Core DbContext object. You should change your Context inheritance to IdentityDbContext<IdentityUser> as below code.
It required below packages, add below nuget pages in our project.
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
This is important because when Identity module generated, its looking for DbContext object which inherit from IdentityDbContext due to create identity tables for target DbContext object over this.
Scaffolding ASP.NET Core Identity
In order to create pre-built Login pages, follow the below steps;
Right Click Pages Folder -> Add -> New Scaffolding Item -> Identity -> Identity -> Click Add
Following screen, Select Login/Logout/Register -> Select Data context class -> Click Add
After applying these steps finally the Login pages generated from Visual Studio and should be added Areas folder as below image;
This is the ASP.NET Core Identity folder structure which include Login pages under the Areas->Identity->Pages->Account. It can be change but we leave as it is.
Starting with underscore (_) pages are special pages for asp.net core Razor template. We explain these pages later, you can see the Login / Logout / Register pages are ready for our project.
You can remove IdentityHostingStartup class, we don’t need to use for our project now.
One of the generated file is ScaffoldingReadme.txt. So this is instruction of how we activate Identity module, and you can this txt file. After following this text file Open Startup.cs — Configure() method. Method should be same as below respectively;
Now we can put the authorization our pages in order to force login to the user. Its very easy to set-up authorization into Razor pages. In example lets use the Cart-Compare-Wishlist page.
Go To Product folder -> Open Cart.cshtml.cs -> Add Authorize Attribute at the top of the class.
Lets run the application and try to open Product Page;
This error says that you are configured ASP.NET Identity module has not default scheme. In order to provide default scheme we should configure Identity into Startup — ConfigureServices() method as below code.
Basically this code configure User entity, UI option and persistent database options. Also you can configure Password options over the IdentityOptions.
When we configure Identity with Entity Framework Core with this code; .AddEntityFrameworkStores<AspnetRunContext>();
That means we should also extend our database with Identity tables.
Update Database with Identity Tables
ASP.NET Identity module finished but not reflected to the database yet, so we should update database with ASP.NET Identity tables in order to use these tables through the application.
We followed the same steps when update the database; Run the following commands in the Package Manager Console:
Add-Migration IdentityAdded
Update-Database
You can see new Migration class added under the Migrations folder related with IdentityAdded operation. Also You can see from Sql Server Object Explorer that the Identity tables is added to the database;
Now, Run the application Click the Products link and see the result as below;
Anymore for Products page, without login users cannot be access.
Its not meaningfully when e-commerce application authorize Product page but we will use an example of authorization. You can change your authorized page as per your design.
Adapt UI Pages with E&E E-Commerce Bootstrap 4 Web Template
Since here we developed Product Data and Business Layer and also apply ASP.NET Identity module in our project. So in presentation layer we should apply some theme and layouts for listing this categories and its products on the page.
So the next steps should cover below parts;
- E&E E-Commerce Bootstrap 4 Theme Implementation
- ASP.NET Core Razor Tools — View Components, partial Views, Tag Helpers, Model Bindings and Validations, Razor Sections etc..
For the rest of the application, I recommend to follow aspnetrun e-books in order to develop step by step a real-world application in every level of asp.net core.
Conclusion
This article demonstrate that how to build a real world e-commerce web application in a layered architecture project with ASP.NET Core & Entity Framework Core which used main components; razor pages, middlewares, dependency injection, configuration, logging. Using Domain Driven Design Layers and every layer has own test project and built-in test methods which you can continue to provide your custom use cases.
For the rest of the application, I recommend to follow aspnetrun e-books in order to develop step by step a real-world application in every level of asp.net core.
Whats Next ?
You can follow this article series with implementation of this Basic repository in a real-world example.
Continue with next article which explained implementation of this repository.
What is AspnetRun ?
A starter kit for your next ASP.NET Core web application. Boilerplate for ASP.NET Core reference application with Entity Framework Core, demonstrating a layered application architecture with DDD best practices.
There are 6 asp.net core repositories from beginner to senior level in order to leverage your asp.net core skills and demonstrate real-world implementations.