💠 Clean Architecture End To End In .NET 5

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 officially released in the month of 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?
  1. Entity Framework Code First Approach 
  2. Dependency Injection
  3. Automapper
  4. JWT Authentication
  5. Versioning of API's
  6. Swagger (Versioning)
Packages used in this Project!
  1. AutoMapper.Extensions.Microsoft.DependencyInjection
  2. Microsoft.AspNetCore.Authentication.JwtBearer
  3. Microsoft.AspNetCore.Mvc.Versioning
  4. Microsoft.EntityFrameworkCore
  5. Microsoft.EntityFrameworkCore.Design
  6. Microsoft.EntityFrameworkCore.Relational
  7. Microsoft.EntityFrameworkCore.SqlServer
  8. Microsoft.EntityFrameworkCore.Tools
  9. Newtonsoft.Json
  10. Swashbuckle.AspNetCore
  11. 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 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:
  1. using DataAccessLayer.EntityMappers;  
  2. using DataAccessLayer.Models;  
  3. using DataAccessLayer.SeedData;  
  4. using Microsoft.EntityFrameworkCore;  
  5. using System;  
  6. using System.Collections.Generic;  
  7. using System.Text;  
  8.   
  9. namespace DataAccessLayer.ApplicationDbContext  
  10. {  
  11.     public partial class CFCDbContext : DbContext  
  12.     {  
  13.         public CFCDbContext(DbContextOptions options) : base(options)  
  14.         {  
  15.   
  16.         }  
  17.         public DbSet<User> users { getset; }  
  18.         public DbSet<UserRoles> userRoles { getset; }  
  19.   
  20.         protected override void OnModelCreating(ModelBuilder modelBuilder)  
  21.         {  
  22.             modelBuilder.ApplyConfiguration(new UserMap());  
  23.             modelBuilder.ApplyConfiguration(new UserRoleMap());  
  24.             modelBuilder.ApplyConfiguration(new BranchMap());  
  25.             base.OnModelCreating(modelBuilder);  
  26.             modelBuilder.Seed();  
  27.         }  
  28.     }  
  29. }  
Entity Mappers
 
Creating a Tables with relations using Model Objects 
  1. using DataAccessLayer.Models;  
  2. using Microsoft.EntityFrameworkCore;  
  3. using Microsoft.EntityFrameworkCore.Metadata.Builders;  
  4.   
  5. namespace DataAccessLayer.EntityMappers  
  6. {  
  7.     public class BranchMap : IEntityTypeConfiguration<Branches>  
  8.     {  
  9.         public void Configure(EntityTypeBuilder<Branches> builder)  
  10.         {  
  11.             builder.ToTable("branches");  
  12.             builder.HasKey(x => x.BranchId)  
  13.                    .HasName("pk_branch_id");  
  14.             builder.Property(x => x.BranchId)  
  15.                   .ValueGeneratedOnAdd()  
  16.                   .HasColumnName("branch_id")  
  17.                   .HasColumnType("INT");  
  18.             builder.Property(x => x.BranchName)  
  19.                   .HasColumnName("branch_name")  
  20.                   .HasColumnType("NVARCHAR(100)")  
  21.                   .IsRequired();  
  22.             builder.Property(x => x.BranchManager)  
  23.                  .HasColumnName("branch_manager")  
  24.                   .HasColumnType("NVARCHAR(100)")  
  25.                   .IsRequired();  
  26.             builder.Property(x => x.BranchLocation)  
  27.                  .HasColumnName("branch_location")  
  28.                   .HasColumnType("NVARCHAR(100)")  
  29.                   .IsRequired();  
  30.             builder.Property(x => x.BranchNumber)  
  31.                  .HasColumnName("branch_number")  
  32.                   .HasColumnType("BIGINT")  
  33.                   .IsRequired();  
  34.             builder.Property(x => x.CreatedDate)  
  35.                  .HasColumnName("created_date")  
  36.                  .HasColumnType("DATETIME");  
  37.             builder.Property(x => x.ModifiedDate)  
  38.                  .HasColumnName("modified_date")  
  39.                  .HasColumnType("DATETIME");  
  40.             builder.Property(x => x.IsActive)  
  41.                  .HasColumnName("is_active")  
  42.                  .HasColumnType("BIT");  
  43.         }  
  44.     }  
  45. }  
Migrations
 
Includes all our Migrations respective to tables which we are consuming  
 
Models
 
Defining the Table Models using Classes 
  1. using Newtonsoft.Json;  
  2. using System;  
  3. using System.Collections.Generic;  
  4. using System.Text;  
  5.   
  6. namespace DataAccessLayer.Models  
  7. {  
  8.    public class Branches : BaseModel  
  9.     {  
  10.         [JsonProperty(PropertyName = "branch_id")]  
  11.         public int BranchId { getset; }  
  12.         [JsonProperty(PropertyName = "branch_name")]  
  13.         public string BranchName { getset; }  
  14.         [JsonProperty(PropertyName = "branch_manager")]  
  15.         public string BranchManager { getset; }  
  16.         [JsonProperty(PropertyName = "branch_number")]  
  17.         public long BranchNumber { getset; }  
  18.         [JsonProperty(PropertyName = "branch_location")]  
  19.         public string BranchLocation { getset; }  
  20.     }  
  21. }  
Seed Data
 
Static Data for Tables. 
  1. using DataAccessLayer.Models;  
  2. using Microsoft.EntityFrameworkCore;  
  3. using System;  
  4. using System.Collections.Generic;  
  5. using System.Text;  
  6.   
  7. namespace DataAccessLayer.SeedData  
  8. {  
  9.     public static class ModelBuilderExtension  
  10.     {  
  11.         public static void Seed(this ModelBuilder modelBuilder)  
  12.         {  
  13.             // Seed Data for Admin Roles  
  14.             modelBuilder.Entity<UserRoles>().HasData(  
  15.                 new UserRoles { RoleId = 1,RoleName = "SuperAdmin",IsActive = true  },  
  16.                 new UserRoles { RoleId = 2,RoleName = "Admin",IsActive = true  }  
  17.             );  
  18.         }  
  19.     }  
  20. }  
Folder Structure 
 
 
Here i am maintaining the Folder Structure to have a deeper undestanding and naming conventions as per my standard.
 
Dependency Injection
 
Create a Class Library(.Net Core) named as Services in which we are maintaing all the Business logic and Core Functionality.
 
Folder Structure 
 
 
 
Mapper : Automapper - Obeject - Object Mapping   
  1. using AutoMapper;  
  2. using DataAccessLayer.Models;  
  3. using System;  
  4. using System.Collections.Generic;  
  5. using System.Text;  
  6. using static Services.ViewModels.CommonModel;  
  7.   
  8. namespace Services.Mapper  
  9. {  
  10.    public class Mapper : Profile  
  11.     {  
  12.         public Mapper()  
  13.         {  
  14.             AllowNullDestinationValues = true;  
  15.             //Source -> Destination  
  16.             CreateMap<UserRoles, RolesModel>()  
  17.                 .ForMember(dto => dto.RoleId, opt => opt.MapFrom(src => src.RoleId))  
  18.                 .ForMember(dto => dto.RoleName, opt => opt.MapFrom(src => src.RoleName));  
  19.             CreateMap<User, LoginModel>()  
  20.                 .ForMember(dto => dto.UserName, opt => opt.MapFrom(src => src.Email));  
  21.         }  
  22.     }  
  23. }  
Repository Pattern using Interfaces
  1. using DataAccessLayer.ApplicationDbContext;  
  2. using System;  
  3. using System.Collections.Generic;  
  4. using System.Text;  
  5. using System.Threading.Tasks;  
  6. using DataAccessLayer.Models;  
  7. using System.Linq;  
  8. using static Services.ViewModels.CommonModel;  
  9. using AutoMapper;  
  10. using Microsoft.EntityFrameworkCore;  
  11. using System.Security.Claims;  
  12. using System.IdentityModel.Tokens.Jwt;  
  13. using Microsoft.IdentityModel.Tokens;  
  14. using Microsoft.Extensions.Configuration;  
  15.   
  16. namespace Services.RepositoryPattern.UserLogin  
  17. {  
  18.     public class UserService : IUserService  
  19.     {  
  20.         #region Property  
  21.         private readonly CFCDbContext _cFCDbContext;  
  22.         private readonly IMapper _mapper;  
  23.         
  24.         #endregion  
  25.  
  26.         #region Constructor  
  27.         public UserService(CFCDbContext cFCDbContext, IMapper mapper)  
  28.         {  
  29.             _cFCDbContext = cFCDbContext;  
  30.             _mapper = mapper;  
  31.               
  32.         }  
  33.         #endregion  
  34.  
  35.         #region Get User Roles  
  36.         /// <summary>  
  37.         /// Get User Roles from Db  
  38.         /// </summary>  
  39.         /// <returns></returns>  
  40.         public async Task<List<RolesModel>> GetUserRolesAsync()  
  41.         {  
  42.             try  
  43.             {  
  44.                 var userRoles = await _cFCDbContext.userRoles.Where(c => c.IsActive.Equals(true)).ToListAsync();  
  45.                 return _mapper.Map<List<RolesModel>>(userRoles);  
  46.             }  
  47.             catch (Exception ex)  
  48.             {  
  49.                 throw ex;  
  50.             }  
  51.         }  
  52.         #endregion  
Common model
  1. using Newtonsoft.Json;  
  2. using System;  
  3. using System.Collections.Generic;  
  4. using System.ComponentModel.DataAnnotations;  
  5. using System.Text;  
  6.   
  7. namespace Services.ViewModels  
  8. {  
  9.    public class CommonModel  
  10.     {  
  11.         public class UserModel  
  12.         {  
  13.             [JsonProperty(PropertyName = "firstname")]  
  14.             [Required]  
  15.             public string FirstName { getset; }  
  16.             [JsonProperty(PropertyName = "lastname")]  
  17.             [Required]  
  18.             public string LastName { getset; }  
  19.             [JsonProperty(PropertyName = "phonenumber")]  
  20.             [Required]  
  21.             public long PhoneNumber { getset; }  
  22.             [JsonProperty(PropertyName = "password")]  
  23.             [Required]  
  24.             public string Password { getset; }  
  25.            [JsonProperty(PropertyName = "email")]  
  26.             [Required]  
  27.             public string Email { getset; }  
  28.             [JsonProperty(PropertyName = "rolename")]  
  29.             [Required]  
  30.             public string RoleName { getset; }  
  31.         }  
  32.   
  33.         public class RolesModel  
  34.         {  
  35.             [JsonProperty(PropertyName = "role_id")]  
  36.             public int RoleId { getset; }  
  37.             [JsonProperty(PropertyName = "role_name")]  
  38.             public string RoleName { getset; }  
  39.         }  
  40.         public class LoginModel  
  41.         {  
  42.             [JsonProperty(PropertyName = "username")]  
  43.             [Required]  
  44.             public string UserName { getset; }  
  45.             [JsonProperty(PropertyName = "password")]  
  46.             [Required]  
  47.             public string Password { getset; }  
  48.         }  
  49.           
  50.     }  
  51. }  
JWT Authentication, Swagger & Versioning
 
I followed C# Regions to improve the code readability, so in this configure services, I have separated everything with regions 
 
Startup.cs
  1. using AutoMapper;  
  2. using CFC_API.Versioning;  
  3. using DataAccessLayer.ApplicationDbContext;  
  4. using Services.RepositoryPattern.UserLogin;  
  5. using DataAccessLayer.ApplicationDbContext;  
  6. using Microsoft.AspNetCore.Authentication.JwtBearer;  
  7. using Microsoft.AspNetCore.Builder;  
  8. using Microsoft.AspNetCore.Hosting;  
  9. using Microsoft.AspNetCore.HttpsPolicy;  
  10. using Microsoft.AspNetCore.Identity;  
  11. using Microsoft.AspNetCore.Mvc;  
  12. using Microsoft.AspNetCore.Mvc.Versioning;  
  13. using Microsoft.EntityFrameworkCore;  
  14. using Microsoft.Extensions.Configuration;  
  15. using Microsoft.Extensions.DependencyInjection;  
  16. using Microsoft.Extensions.Hosting;  
  17. using Microsoft.Extensions.Logging;  
  18. using Microsoft.IdentityModel.Tokens;  
  19. using Microsoft.OpenApi.Models;  
  20. using Services.RepositoryPattern.UserLogin;  
  21. using System;  
  22. using System.Collections.Generic;  
  23. using System.IO;  
  24. using System.Linq;  
  25. using System.Reflection;  
  26. using System.Text;  
  27. using System.Threading.Tasks;  
  28.   
  29. namespace CleanArchitecture  
  30. {  
  31.     public class Startup  
  32.     {  
  33.         public Startup(IConfiguration configuration)  
  34.         {  
  35.             Configuration = configuration;  
  36.         }  
  37.   
  38.         public IConfiguration Configuration { get; }  
  39.   
  40.         // This method gets called by the runtime. Use this method to add services to the container.  
  41.         public void ConfigureServices(IServiceCollection services)  
  42.         {  
  43.   
  44.             services.AddControllers();  
  45.  
  46.             #region API Versioning  
  47.             services.AddApiVersioning(options =>  
  48.             {  
  49.                 options.ReportApiVersions = true;  
  50.                 options.DefaultApiVersion = new ApiVersion(1, 0);  
  51.                 options.AssumeDefaultVersionWhenUnspecified = true;  
  52.                 options.ApiVersionReader =  
  53.                   new HeaderApiVersionReader("X-API-Version");  
  54.             });  
  55.             #endregion  
  56.  
  57.             #region Connection String  
  58.             services.AddDbContext<CFCDbContext>(item => item.UseSqlServer(Configuration.GetConnectionString("myconn")));  
  59.             #endregion  
  60.  
  61.             #region Enable Cors   
  62.             services.AddCors();  
  63.             #endregion  
  64.  
  65.             #region Swagger  
  66.             services.AddSwaggerGen(swagger =>  
  67.             {  
  68.                 swagger.SwaggerDoc("v1"new OpenApiInfo  
  69.                 {  
  70.                     Version = "v1",  
  71.                     Title = " Clean Architecture v1 API's",  
  72.                     Description = $"Clean Architecture API's for integration with UI \r\n\r\n © Copyright {DateTime.Now.Year} JK. All rights reserved."  
  73.                 });  
  74.                 swagger.SwaggerDoc("v2"new OpenApiInfo  
  75.                 {  
  76.                     Version = "v2",  
  77.                     Title = "Clean Architecture v2 API's",  
  78.                     Description = $"Clean Architecture API's for integration with UI \r\n\r\n © Copyright {DateTime.Now.Year} JK. All rights reserved."  
  79.                 });  
  80.                 swagger.ResolveConflictingActions(a => a.First());  
  81.                 swagger.OperationFilter<RemoveVersionFromParameterv>();  
  82.                 swagger.DocumentFilter<ReplaceVersionWithExactValueInPath>();  
  83.  
  84.                 #region Enable Authorization using Swagger (JWT)   
  85.                 swagger.AddSecurityDefinition("Bearer"new OpenApiSecurityScheme()  
  86.                 {  
  87.                     Name = "Authorization",  
  88.                     Type = SecuritySchemeType.ApiKey,  
  89.                     Scheme = "Bearer",  
  90.                     BearerFormat = "JWT",  
  91.                     In = ParameterLocation.Header,  
  92.                     Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\nExample: \"Bearer 12345abcdef\"",  
  93.                 });  
  94.   
  95.                 swagger.AddSecurityRequirement(new OpenApiSecurityRequirement  
  96.                 {  
  97.                     {  
  98.                           new OpenApiSecurityScheme  
  99.                             {  
  100.                                 Reference = new OpenApiReference  
  101.                                 {  
  102.                                     Type = ReferenceType.SecurityScheme,  
  103.                                     Id = "Bearer"  
  104.                                 }  
  105.                             },  
  106.                             new string[] {}  
  107.   
  108.                     }  
  109.                 });  
  110.                 #endregion  
  111.             });  
  112.             #endregion  
  113.  
  114.             #region Swagger Json property Support  
  115.             services.AddSwaggerGenNewtonsoftSupport();  
  116.             #endregion  
  117.  
  118.             #region JWT   
  119.   
  120.             // Adding Authentication    
  121.             services.AddAuthentication(options =>  
  122.             {  
  123.                 options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;  
  124.                 options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;  
  125.                 options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;  
  126.             })  
  127.   
  128.             // Adding Jwt Bearer    
  129.             .AddJwtBearer(options =>  
  130.             {  
  131.                 options.SaveToken = true;  
  132.                 options.RequireHttpsMetadata = false;  
  133.                 options.TokenValidationParameters = new TokenValidationParameters()  
  134.                 {  
  135.                     ValidateIssuer = true,  
  136.                     ValidateAudience = true,  
  137.                     ValidAudience = Configuration["Jwt:Issuer"],  
  138.                     ValidIssuer = Configuration["Jwt:Issuer"],  
  139.                     IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))  
  140.                 };  
  141.             });  
  142.             #endregion  
  143.  
  144.             #region Dependency Injection  
  145.             services.AddTransient<IUserService, UserService>();  
  146.             #endregion  
  147.  
  148.             #region Automapper  
  149.             services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());  
  150.             #endregion  
  151.         }  
  152.   
  153.         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.  
  154.         public void Configure(IApplicationBuilder app, IWebHostEnvironment env)  
  155.         {  
  156.             if (env.IsDevelopment())  
  157.             {  
  158.                 app.UseDeveloperExceptionPage();  
  159.                 app.UseSwagger();  
  160.                 app.UseSwaggerUI(c =>  
  161.                 {  
  162.                 c.SwaggerEndpoint("/swagger/v1/swagger.json""API v1");  
  163.                 c.SwaggerEndpoint("/swagger/v2/swagger.json""API v2");  
  164.                 }) ;  
  165.             }  
  166.   
  167.             app.UseHttpsRedirection();  
  168.   
  169.             app.UseRouting();  
  170.   
  171.             app.UseAuthentication();  
  172.   
  173.             app.UseAuthorization();  
  174.  
  175.             #region Global Cors Policy  
  176.             app.UseCors(x => x  
  177.                 .AllowAnyMethod()  
  178.                 .AllowAnyHeader()  
  179.                 .SetIsOriginAllowed(origin => true// allow any origin  
  180.                 .AllowCredentials()); // allow credentials  
  181.             #endregion  
  182.   
  183.             app.UseEndpoints(endpoints =>  
  184.             {  
  185.                 endpoints.MapControllers();  
  186.             });  
  187.         }  
  188.     }  
  189. }  
appsettings.json
  1. {  
  2.   "Logging": {  
  3.     "LogLevel": {  
  4.       "Default""Information",  
  5.       "Microsoft""Warning",  
  6.       "Microsoft.Hosting.Lifetime""Information"  
  7.     }  
  8.   },  
  9.   "AllowedHosts""*",  
  10.   "Jwt": {  
  11.     "Key""BB698DAF-6E3F-45FF-8493-06ECCF2F60D0",  
  12.     "Issuer""https://localhost:44393",  
  13.   },  
  14.   "ConnectionStrings": {  
  15.     "myconn""server= Your Connection String; database=CFCDb;Trusted_Connection=True;"  
  16.   }  
  17. }  
Used to store configuration settings such as database connections strings, any application scope global variables. 
 
UserController
  1. using Services.RepositoryPattern.UserLogin;  
  2. using Microsoft.AspNetCore.Authorization;  
  3. using Microsoft.AspNetCore.Http;  
  4. using Microsoft.AspNetCore.Mvc;  
  5. using Microsoft.Extensions.Configuration;  
  6. using Microsoft.IdentityModel.Tokens;  
  7. using System;  
  8. using System.Collections.Generic;  
  9. using System.IdentityModel.Tokens.Jwt;  
  10. using System.Linq;  
  11. using System.Security.Claims;  
  12. using System.Text;  
  13. using System.Threading.Tasks;  
  14. using static Services.ViewModels.CommonModel;  
  15.   
  16. namespace CleanArchitecture.Controllers  
  17. {  
  18.     [ApiVersion("1.0")]  
  19.     [ApiExplorerSettings(GroupName = "v1")]  
  20.     public class UserController : BaseController  
  21.     {  
  22.         #region Property  
  23.         private readonly IUserService _userService;  
  24.         private readonly IConfiguration _configuration;  
  25.         #endregion  
  26.  
  27.         #region Constructor  
  28.         public UserController(IUserService userService, IConfiguration configuration)  
  29.         {  
  30.             _userService = userService;  
  31.             _configuration = configuration;  
  32.         }  
  33.         #endregion  
  34.  
  35.         #region Create User  
  36.         /// <summary>  
  37.         /// To Create a User  
  38.         /// </summary>  
  39.         /// <param name="userModel"></param>  
  40.         /// <returns></returns>  
  41.         [HttpPost(nameof(CreateUser))]  
  42.         public async Task<IActionResult> CreateUser([FromBody]UserModel userModel)  
  43.         {  
  44.             try  
  45.             {  
  46.                 if (ModelState.IsValid)  
  47.                 {  
  48.                     var result = await _userService.CreateUserAsync(userModel);  
  49.                     return Ok(result);  
  50.                 }  
  51.                 else  
  52.                 {  
  53.                     return BadRequest("Please fill all the required parameters");  
  54.                 }  
  55.   
  56.             }  
  57.             catch (Exception ex)  
  58.             {  
  59.                 return BadRequest(ex);  
  60.                 throw;  
  61.             }  
  62.         }  
  63.         #endregion  
  64.  
  65.         #region User Login  
  66.         /// <summary>  
  67.         /// Login Authentication  
  68.         /// </summary>  
  69.         /// <param name="loginModel"></param>  
  70.         /// <returns></returns>  
  71.         [HttpPost(nameof(Login)), AllowAnonymous]  
  72.         public async Task<IActionResult> Login([FromBody]LoginModel loginModel)  
  73.         {  
  74.             try  
  75.             {  
  76.                 var response = await _userService.UserLoginAsync(loginModel);  
  77.                 if (response is true)  
  78.                 {  
  79.                     var userRoles = await _userService.GetUserRolesAsync();  
  80.                     var authClaims = new List<Claim>  
  81.                              {  
  82.                     new Claim(ClaimTypes.Name, loginModel.UserName),  
  83.                     new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),  
  84.                              };  
  85.   
  86.                     foreach (var userRole in userRoles)  
  87.                     {  
  88.                         authClaims.Add(new Claim(ClaimTypes.Role, userRole.RoleName));  
  89.                     }  
  90.   
  91.                     var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));  
  92.   
  93.                     var token = new JwtSecurityToken(  
  94.                         issuer: _configuration["Jwt:Issuer"],  
  95.                         audience: _configuration["Jwt:Issuer"],  
  96.                         expires: DateTime.Now.AddHours(3),  
  97.                         claims: authClaims,  
  98.                         signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)  
  99.                         );  
  100.                     return Ok(new  
  101.                     {  
  102.                         token = new JwtSecurityTokenHandler().WriteToken(token),  
  103.                         expiration = token.ValidTo  
  104.                     });  
  105.                 }  
  106.                 return Unauthorized();  
  107.   
  108.             }  
  109.   
  110.             catch (Exception ex)  
  111.             {  
  112.                 return BadRequest(ex);  
  113.                 throw;  
  114.             }  
  115.         }  
  116.         #endregion  
  117.     }  
  118. }  
BaseController
  1. using Microsoft.AspNetCore.Authorization;  
  2. using Microsoft.AspNetCore.Http;  
  3. using Microsoft.AspNetCore.Mvc;  
  4. using System;  
  5. using System.Collections.Generic;  
  6. using System.Linq;  
  7. using System.Net;  
  8. using System.Threading.Tasks;  
  9. using static CleanArchitecture.ViewModels.Common.ResultModel;  
  10.   
  11. namespace CleanArchitecture.Controllers  
  12. {  
  13.     [Route("api/v{version:apiversion}/[controller]")]  
  14.     [Authorize(AuthenticationSchemes = Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme)]  
  15.     public class BaseController : ControllerBase  
  16.     {  
  17.         #region Protected Members  
  18.         /// <summary>  
  19.         /// Detailed Exception  
  20.         /// </summary>  
  21.         /// <param name="ex"></param>  
  22.         /// <returns></returns>  
  23.         protected object DetailedException(Exception ex)  
  24.         {  
  25.             var errormessage = ex.Message;  
  26.             if (ex.InnerException != null)  
  27.             {  
  28.                 errormessage = "\n\nException: " + GetInnerException(ex);  
  29.             }  
  30.             var result = new Result  
  31.             {  
  32.                 status = new Status  
  33.                 {  
  34.                     code = (int)HttpStatusCode.InternalServerError,  
  35.                     message = errormessage  
  36.                 }  
  37.             };  
  38.             return result;  
  39.         }  
  40.   
  41.         /// <summary>  
  42.         /// Get Inner Exception  
  43.         /// </summary>  
  44.         /// <param name="ex"></param>  
  45.         /// <returns></returns>  
  46.         private string GetInnerException(Exception ex)  
  47.         {  
  48.             if (ex.InnerException != null)  
  49.             {  
  50.                 return  
  51.                     $"{ex.InnerException.Message + "( \n " + ex.Message + " \n )"} > {GetInnerException(ex.InnerException)} ";  
  52.             }  
  53.             return string.Empty;  
  54.         }  
  55.         #endregion  
  56.     }  
  57. }  
 RolesController
  1. using Microsoft.AspNetCore.Http;  
  2. using Microsoft.AspNetCore.Mvc;  
  3. using Services.RepositoryPattern.UserLogin;  
  4. using System;  
  5. using System.Collections.Generic;  
  6. using System.Linq;  
  7. using System.Threading.Tasks;  
  8.   
  9. namespace CleanArchitecture.Controllers  
  10. {  
  11.     [ApiVersion("2.0")]  
  12.     [ApiExplorerSettings(GroupName = "v2")]  
  13.     public class RoleController : BaseController  
  14.     {  
  15.         #region Property  
  16.         private readonly IUserService _userService;  
  17.         #endregion  
  18.  
  19.         #region Constructor  
  20.         public RoleController(IUserService userService)  
  21.         {  
  22.             _userService = userService;  
  23.         }  
  24.         #endregion  
  25.  
  26.         #region GetRoles  
  27.         /// <summary>  
  28.         /// Get the User Roles   
  29.         /// </summary>  
  30.         /// <returns></returns>  
  31.         [HttpGet(nameof(GetUserRoles))]  
  32.         public async Task<IActionResult> GetUserRoles()  
  33.         {  
  34.             try  
  35.             {  
  36.                 var result = await _userService.GetUserRolesAsync();  
  37.                 if (result is not nullreturn Ok(result); else return BadRequest("No Data Found");  
  38.             }  
  39.             catch (Exception ex)  
  40.             {  
  41.                 return BadRequest(ex);  
  42.                 throw;  
  43.             }  
  44.         }  
  45.         #endregion  
  46.     }  
  47. }  
Basically, I have set up everything in the BaseController and invoking this base to all the controllers in order to authorize and for routing. Below is the GitHub link to clone the project.
 
 
This is the entire end to end clean architecture with the latest .NET 5, I hope this article helps you.
 
Keep learning!!!