Introduction
Hello everyone, in this article we are going to cover clean architecture with end-to-end support in ASP.NET 5.0. As we all know, its newly launched Framework was officially released in November. Here I am sharing the link to install the SDK for .NET 5
What we are going to cover in this .NET 5 Clean Architecture?
- Entity Framework Code First Approach
- Dependency Injection
- Automapper
- JWT Authentication
- Versioning of API's
- Swagger (Versioning)
Packages used in this Project!
- AutoMapper.Extensions.Microsoft.DependencyInjection
- Microsoft.AspNetCore.Authentication.JwtBearer
- Microsoft.AspNetCore.Mvc.Versioning
- Microsoft.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.Design
- Microsoft.EntityFrameworkCore.Relational
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tools
- Newtonsoft.Json
- Swashbuckle.AspNetCore
- Swashbuckle.AspNetCore.Newtonsoft
Step 1. Create a Project in Visual Studio.

Step 2

Make Sure to Select the ASP.NET Core 5.0 and enabling the OpenAPI support helps to add the swagger by default in our project without installing it manually again.
Step 3. Entity Framework Code First Approach
Create a Class Library (.NET Core) named DataAccessLayer, which contains:
ApplicationDbContext
Wrapping all the classes using conventions.
using DataAccessLayer.EntityMappers;
using DataAccessLayer.Models;
using DataAccessLayer.SeedData;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Text;
namespace DataAccessLayer.ApplicationDbContext
{
public partial class CFCDbContext : DbContext
{
public CFCDbContext(DbContextOptions options) : base(options)
{
}
public DbSet<User> users { get; set; }
public DbSet<UserRoles> userRoles { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new UserMap());
modelBuilder.ApplyConfiguration(new UserRoleMap());
modelBuilder.ApplyConfiguration(new BranchMap());
base.OnModelCreating(modelBuilder);
modelBuilder.Seed();
}
}
}
Entity mappers
Creating Tables with relations using Model Objects
using DataAccessLayer.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace DataAccessLayer.EntityMappers
{
public class BranchMap : IEntityTypeConfiguration<Branches>
{
public void Configure(EntityTypeBuilder<Branches> builder)
{
builder.ToTable("branches");
builder.HasKey(x => x.BranchId)
.HasName("pk_branch_id");
builder.Property(x => x.BranchId)
.ValueGeneratedOnAdd()
.HasColumnName("branch_id")
.HasColumnType("INT");
builder.Property(x => x.BranchName)
.HasColumnName("branch_name")
.HasColumnType("NVARCHAR(100)")
.IsRequired();
builder.Property(x => x.BranchManager)
.HasColumnName("branch_manager")
.HasColumnType("NVARCHAR(100)")
.IsRequired();
builder.Property(x => x.BranchLocation)
.HasColumnName("branch_location")
.HasColumnType("NVARCHAR(100)")
.IsRequired();
builder.Property(x => x.BranchNumber)
.HasColumnName("branch_number")
.HasColumnType("BIGINT")
.IsRequired();
builder.Property(x => x.CreatedDate)
.HasColumnName("created_date")
.HasColumnType("DATETIME");
builder.Property(x => x.ModifiedDate)
.HasColumnName("modified_date")
.HasColumnType("DATETIME");
builder.Property(x => x.IsActive)
.HasColumnName("is_active")
.HasColumnType("BIT");
}
}
}
Migrations
Includes all our Migrations respective to tables that we are consuming
Models
Defining the Table Models Using Classes
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
namespace DataAccessLayer.Models
{
public class Branches : BaseModel
{
[JsonProperty(PropertyName = "branch_id")]
public int BranchId { get; set; }
[JsonProperty(PropertyName = "branch_name")]
public string BranchName { get; set; }
[JsonProperty(PropertyName = "branch_manager")]
public string BranchManager { get; set; }
[JsonProperty(PropertyName = "branch_number")]
public long BranchNumber { get; set; }
[JsonProperty(PropertyName = "branch_location")]
public string BranchLocation { get; set; }
}
}
Seed Data
Static Data for Tables
using DataAccessLayer.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Text;
namespace DataAccessLayer.SeedData
{
public static class ModelBuilderExtension
{
public static void Seed(this ModelBuilder modelBuilder)
{
// Seed Data for Admin Roles
modelBuilder.Entity<UserRoles>().HasData(
new UserRoles { RoleId = 1, RoleName = "SuperAdmin", IsActive = true },
new UserRoles { RoleId = 2, RoleName = "Admin", IsActive = true }
);
}
}
}
Folder structure

Here I am maintaining the Folder Structure to have a deeper understanding and naming conventions as per my standard.
Dependency injection
Create a Class Library(.Net Core) named Services in which we are maintaining all the Business logic and Core Functionality.
Folder Structure

Mapper
Automapper - Object - Object Mapping.
using AutoMapper;
using DataAccessLayer.Models;
using System;
using System.Collections.Generic;
using System.Text;
using static Services.ViewModels.CommonModel;
namespace Services.Mapper
{
public class Mapper : Profile
{
public Mapper()
{
AllowNullDestinationValues = true;
// Source -> Destination
CreateMap<UserRoles, RolesModel>()
.ForMember(dto => dto.RoleId, opt => opt.MapFrom(src => src.RoleId))
.ForMember(dto => dto.RoleName, opt => opt.MapFrom(src => src.RoleName));
CreateMap<User, LoginModel>()
.ForMember(dto => dto.UserName, opt => opt.MapFrom(src => src.Email));
}
}
}
Repository Pattern using Interfaces
using DataAccessLayer.ApplicationDbContext;
using DataAccessLayer.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using static Services.ViewModels.CommonModel;
using Microsoft.IdentityModel.Tokens;
namespace Services.RepositoryPattern.UserLogin
{
public class UserService : IUserService
{
#region Property
private readonly CFCDbContext _cFCDbContext;
private readonly IMapper _mapper;
#endregion
#region Constructor
public UserService(CFCDbContext cFCDbContext, IMapper mapper)
{
_cFCDbContext = cFCDbContext;
_mapper = mapper;
}
#endregion
#region Get User Roles
/// <summary>
/// Get User Roles from Db
/// </summary>
/// <returns></returns>
public async Task<List<RolesModel>> GetUserRolesAsync()
{
try
{
var userRoles = await _cFCDbContext.userRoles.Where(c => c.IsActive.Equals(true)).ToListAsync();
return _mapper.Map<List<RolesModel>>(userRoles);
}
catch (Exception ex)
{
throw ex;
}
}
#endregion
}
}
Common model
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
namespace Services.ViewModels
{
public class CommonModel
{
public class UserModel
{
[JsonProperty(PropertyName = "firstname")]
[Required]
public string FirstName { get; set; }
[JsonProperty(PropertyName = "lastname")]
[Required]
public string LastName { get; set; }
[JsonProperty(PropertyName = "phonenumber")]
[Required]
public long PhoneNumber { get; set; }
[JsonProperty(PropertyName = "password")]
[Required]
public string Password { get; set; }
[JsonProperty(PropertyName = "email")]
[Required]
public string Email { get; set; }
[JsonProperty(PropertyName = "rolename")]
[Required]
public string RoleName { get; set; }
}
public class RolesModel
{
[JsonProperty(PropertyName = "role_id")]
public int RoleId { get; set; }
[JsonProperty(PropertyName = "role_name")]
public string RoleName { get; set; }
}
public class LoginModel
{
[JsonProperty(PropertyName = "username")]
[Required]
public string UserName { get; set; }
[JsonProperty(PropertyName = "password")]
[Required]
public string Password { get; set; }
}
}
}
JWT Authentication, Swagger & Versioning
I followed C# Regions to improve the code readability, so in this configure service, I have separated everything with regions
Startup.cs

JosephsPosted Dec 22, 2022, 2:26 AM
Token not generated
atulPosted Jan 20, 2021, 2:00 PM
I see that API project has reference of data layer. Ideally this is against the architecture.
RamKumar APosted Jan 19, 2021, 4:17 PM
This is not clean architecture, check this about Clean architecture - https://docs.microsoft.com/en-us/dotnet/architecture/modern-web-apps-azure/media/image5-9.png
Saravanakumar SekaranPosted Jan 2, 2021, 6:52 AM
Awesome Articles
MJ EbrahimiPosted Dec 12, 2020, 8:37 AM
This is 3-Layer architecture not Clean...
Hamid KhanPosted Dec 11, 2020, 4:22 AM
Very good article Mr Jay??
SriPosted Dec 10, 2020, 4:25 AM
Thanks for your efforts Jay Krishna Reddy, This really helps for the folks who are going with .Net 5 in the near future