ASP.NET Core 2.0 User Role Base Menu Management Using Dependency Injection

ASP.NET Core

Introduction

Before we start this article kindly read our previous article,

In our previous article we have discussed in detail about how to use ASP.NET Core Identity in MVC Application for creating user roles and displaying the menu depending on user roles.

In this article we will see in detail how to display role-based dynamic menu after a user logs in. For this we will create a Menu Master table and insert a few records to display the menu and link the URL to the menu based on the logged in user's role.

Here we will see how to:

  • Create default admin and manager users.
  • Create MenuMaster table and insert a few sample records for Admin and Manager roles to display menus.
  • Redirect unauthenticated users to the login page. 
  • Display menu dynamically based on logged in user.

Prerequisites

Make sure you have installed all the prerequisites in your computer. If not, then download and install them all, one by one.

  1. First, download and install Visual Studio 2017 from this link
  2. SQL Server 2014 or above

Using the code

Step 1 - Create a Database

This is in continuation of our previous article; as we have told you  we will be using a Common Database for both ASP.NET Identity tables and for our own new tables.

In our previous article we have explained about creating user roles, and here for role based menu management we need to make a relationship table between ASP.NET Roles table and our menu table.

Let us see in detail about how to create our new Menu Table which has a relationship with ASP.NET Identity AspNetRoles table.

Here we can see the fields used for MenuMaster: 

ASP.NET Core

Firstly, we will create a database and set the connection string in appsettings.json file for DefaultConnection with our new database connection. We will be using this database for ASP.NET Core Identity table creation.

Create Database

Run the following script to create our database MenuMaster table and sample Menu insert rows script. 

  1. USE MASTER         
  2. GO         
  3.          
  4. -- 1) Check for the Database Exists .If the database is exist then drop and create new DB         
  5. IF EXISTS (SELECT [nameFROM sys.databases WHERE [name] = 'AttendanceDB' )         
  6. DROP DATABASE AttendanceDB         
  7. GO         
  8.          
  9. CREATE DATABASE AttendanceDB         
  10. GO         
  11.           
  12.   
  13. USE AttendanceDB      
  14. GO      
  15.     
  16. IF EXISTS ( SELECT [nameFROM sys.tables WHERE [name] = 'MenuMaster' )      
  17. DROP TABLE MenuMaster      
  18. GO      
  19.       
  20. CREATE TABLE MenuMaster      
  21. (      
  22.    MenuIdentity int identity(1,1),      
  23.    MenuID VARCHAR(30)  NOT NULL,      
  24.    MenuName VARCHAR(30)  NOT NULL,    
  25.    Parent_MenuID  VARCHAR(30)  NOT NULL,    
  26.    User_Roll [varchar](256) NOT NULL,     
  27.    MenuFileName VARCHAR(100) NOT NULL,       
  28.    MenuURL VARCHAR(500) NOT NULL,      
  29.    USE_YN Char(1) DEFAULT 'Y',      
  30.    CreatedDate datetime      
  31. CONSTRAINT [PK_MenuMaster] PRIMARY KEY CLUSTERED            
  32. (           
  33.   [MenuIdentity] ASC   ,      
  34.   [MenuID] ASC,      
  35.   [MenuName] ASC        
  36. )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ONON [PRIMARY]           
  37. ON [PRIMARY]         
  38.      
  39. select * from MenuMaster    
  40. -- Insert Admin User Details  
  41. Insert into MenuMaster(MenuID ,MenuName,Parent_MenuID,User_Roll,MenuFileName,MenuURL,USE_YN,CreatedDate)  
  42.     Values('AUSER','ADMIN Dashboard','*','ADMIN','INDEX','ADMINC','Y',getDate())    
  43.  Insert into MenuMaster(MenuID ,MenuName,Parent_MenuID,User_Roll,MenuFileName,MenuURL,USE_YN,CreatedDate)  
  44.     Values('AAbout','About Admin','*','ADMIN','INDEX','ADMINAC','Y',getDate())     
  45. Insert into MenuMaster(MenuID ,MenuName,Parent_MenuID,User_Roll,MenuFileName,MenuURL,USE_YN,CreatedDate)  
  46.     Values('LStock','Live Stock','AUSER','ADMIN','INDEX','StockC','Y',getDate())       
  47. Insert into MenuMaster(MenuID ,MenuName,Parent_MenuID,User_Roll,MenuFileName,MenuURL,USE_YN,CreatedDate)  
  48.     Values('Profile','User Details','AUSER','ADMIN','INDEX','MemberC','Y',getDate())     
  49. Insert into MenuMaster(MenuID ,MenuName,Parent_MenuID,User_Roll,MenuFileName,MenuURL,USE_YN,CreatedDate)  
  50.     Values('MUSER','Manager Dashboard','*','ADMIN','INDEX','ManagerC','Y',getDate())    
  51.  Insert into MenuMaster(MenuID ,MenuName,Parent_MenuID,User_Roll,MenuFileName,MenuURL,USE_YN,CreatedDate)  
  52.     Values('MAbout','About Manager','*','ADMIN','INDEX','ManagerAC','Y',getDate())     
  53. Insert into MenuMaster(MenuID ,MenuName,Parent_MenuID,User_Roll,MenuFileName,MenuURL,USE_YN,CreatedDate)  
  54.     Values('Accounts','Account Details','MUSER','ADMIN','INDEX','AccountC','Y',getDate())      
  55.     Insert into MenuMaster(MenuID ,MenuName,Parent_MenuID,User_Roll,MenuFileName,MenuURL,USE_YN,CreatedDate)  
  56.     Values('Inventory','Inventory Details','MUSER','ADMIN','INDEX','InventoryC','Y',getDate())    
  57.   
  58. -- Insert Manager User Details   
  59. Insert into MenuMaster(MenuID ,MenuName,Parent_MenuID,User_Roll,MenuFileName,MenuURL,USE_YN,CreatedDate)  
  60.     Values('MUSER','Manager Dashboard','*','Manager','INDEX','ManagerC','Y',getDate())    
  61.  Insert into MenuMaster(MenuID ,MenuName,Parent_MenuID,User_Roll,MenuFileName,MenuURL,USE_YN,CreatedDate)  
  62.     Values('MAbout','About Manager','*','Manager','INDEX','ManagerAC','Y',getDate())     
  63. Insert into MenuMaster(MenuID ,MenuName,Parent_MenuID,User_Roll,MenuFileName,MenuURL,USE_YN,CreatedDate)  
  64.     Values('Accounts','Account Details','MUSER','Manager','INDEX','AccountC','Y',getDate())       
  65. Insert into MenuMaster(MenuID ,MenuName,Parent_MenuID,User_Roll,MenuFileName,MenuURL,USE_YN,CreatedDate)  
  66.     Values('Inventory','Inventory Details','MUSER','Manager','INDEX','InventoryC','Y',getDate())     
  67.   
  68.   
  69. select * from MenuMaster   
  70.   
  71. select * from AspnetUserRoles  
  72. --Here we can see the format we are using in our Menu Master table to insert our records for display menu based on user role.  
  • MenuID = 'AUSER' (We will give unique menu ID)
  • MenuName = 'ADMIN Dashboard' (We will give menu display text),
  • Parent_MenuID = '*’ (If this is the main menu then we will give here a “*” otherwise we will give the MenuID of previous records to display this record to show as submenu)
  • User_Roll = 'ADMIN' (Here we will give the User Role, if same menu needs to be used for multiple role based users like Admin, Manager, Accountant and etc. then we will insert the same menu details with different user roles. In our sample we have added the same menu details as 'Manager Dashboard' for both Admin and Manager User as both can view the menu and page.)
  • ,MenuFileName = 'INDEX' (Here we give our View name to be displayed when the menu is clicked)
  • MenuURL = 'ADMINC' (Here we give our Controller name to be displayed when the menu is clicked)
  • USE_YN = 'Y' (This is an optional field as we can use this to display menu or not)
  • CreatedDate = getDate()  (This is also optional)

In this demo application we already have all the needed controllers and views to be displayed when user clicks on menu

Step 2 - Create your ASP.NET Core 

After installing our Visual Studio 2017 click start, then programs and select Visual Studio 2017 - Click Visual Studio 2017. Click New, then Project, select Web and then select ASP.NET Core Web Application. Enter your project name and click. 

ASP.NET Core

Select Web Application(Model-View-Controller) and click on the Change Authentication

ASP.NET Core

Select Individual User Accounts and click ok to create your project

ASP.NET Core

Updating appsettings.json 

In appsettings.json  file we can find the DefaultConnection Connection string. Here in connection string change your SQL Server Name, UID and PWD to create and store all user details in one database. 

  1. "ConnectionStrings": {  
  2.     "DefaultConnection""Server= YOURSERVERNAME;Database=InventoryDB;user id= YOURSQLUSERID;password=YOURSQLPASSWORD;Trusted_Connection=True;MultipleActiveResultSets=true"  
  3.   },  

Step 3 - Add Identity Service in Startup.cs file

By default, in your ASP.NET Core application the Identity Service will be added in Startup.cs File /ConfigureServices method. You can also additionally add the password strength while user registers and also set the default login page/logout page and also AccessDeniedPath by using the following code.

  1. services.AddIdentity<ApplicationUser, IdentityRole>()  
  2.                 .AddEntityFrameworkStores<ApplicationDbContext>()  
  3.                 .AddDefaultTokenProviders();  
  4.   
  5.             //Password Strength Setting  
  6.             services.Configure<IdentityOptions>(options =>  
  7.             {  
  8.                 // Password settings  
  9.                 options.Password.RequireDigit = true;  
  10.                 options.Password.RequiredLength = 8;  
  11.                 options.Password.RequireNonAlphanumeric = false;  
  12.                 options.Password.RequireUppercase = true;  
  13.                 options.Password.RequireLowercase = false;  
  14.                 options.Password.RequiredUniqueChars = 6;  
  15.   
  16.                 // Lockout settings  
  17.                 options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);  
  18.                 options.Lockout.MaxFailedAccessAttempts = 10;  
  19.                 options.Lockout.AllowedForNewUsers = true;  
  20.   
  21.                 // User settings  
  22.                 options.User.RequireUniqueEmail = true;  
  23.             });  
  24.   
  25.             //Seting the Account Login page  
  26.             services.ConfigureApplicationCookie(options =>  
  27.             {  
  28.                 // Cookie settings  
  29.                 options.Cookie.HttpOnly = true;  
  30.                 options.ExpireTimeSpan = TimeSpan.FromMinutes(30);  
  31.                 options.LoginPath = "/Account/Login"// If the LoginPath is not set here, ASP.NET Core will default to /Account/Login  
  32.                 options.LogoutPath = "/Account/Logout"// If the LogoutPath is not set here, ASP.NET Core will default to /Account/Logout  
  33.                 options.AccessDeniedPath = "/Account/AccessDenied"// If the AccessDeniedPath is not set here, ASP.NET Core will default to /Account/AccessDenied  
  34.                 options.SlidingExpiration = true;  
  35.             });  

Step 4 - Register and Create Users

Now our Asp.NET Core web application is ready for users to register in our website and also user can login to our system after registration. We will be doing the Authorization by adding roles to users in next steps. Build and run your application to register your first default Admin user.

ASP.NET Core

 

Here we will be registering two users as one for Admin and another user for Manager. We will be using this user for adding roles. We will create 2 users as [email protected] and [email protected] . Note: You can create users as per your need and change the user details in startup code for adding roles to users.

ASP.NET Core

Refresh the Database

When we refresh our database, we can see all the Identity tables has been created.

ASP.NET Core

Step 5 - Create Role and assign User for Role

We use the below method to create a new Role’s as “Admin” and “Manager;”  we will assign the recently registered users as “Admin”  and  “Manager” to our website. Open Startup.cs file and add this method in your Startup.cs file.

  1. private async Task CreateUserRoles(IServiceProvider serviceProvider)  
  2.     {  
  3.         var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();  
  4.         var UserManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();  
  5.   
  6.         IdentityResult roleResult;  
  7.         //Adding Addmin Role    
  8.         var roleCheck = await RoleManager.RoleExistsAsync("Admin");  
  9.         if (!roleCheck)  
  10.         {  
  11.             //create the roles and seed them to the database    
  12.             roleResult = await RoleManager.CreateAsync(new IdentityRole("Admin"));  
  13.         }  
  14.   
  15.         roleCheck = await RoleManager.RoleExistsAsync("Manager");  
  16.         if (!roleCheck)  
  17.         {  
  18.             //create the roles and seed them to the database    
  19.             roleResult = await RoleManager.CreateAsync(new IdentityRole("Manager"));  
  20.         }  
  21.   
  22.         //Assign Admin role to the main User here we have given our newly loregistered login id for Admin management    
  23.         ApplicationUser user = await UserManager.FindByEmailAsync("[email protected]");  
  24.         var User = new ApplicationUser();  
  25.         await UserManager.AddToRoleAsync(user, "Admin");  
  26.   
  27.         user = await UserManager.FindByEmailAsync("[email protected]");  
  28.         await UserManager.AddToRoleAsync(user, "Manager");  
  29.   
  30.     }   

From Startup.cs file we can find the Configure method. Call our CreateUserRoles method from this Configure method. When we build and run our application we can see new Role as “Admin” and “Manager” will be created in ASPNetRole table.  

Step 6 - Create Admin/Manager Page and Set Authorization

Now we have a Admin/Manager user for our ASP.NET Core web application. As a next step let's create Controllers and views to display based on user login. In our previous example we have already seen how to set Authorization for roles in each page. Using that we will be creating all our Controllers and Views. In the attached sample demo application you can find all the controllers and views which we have created and create your own as per your need.  

Step 7 - Working with Dependency Injection for Menu Display

Creating Model Class

First, we will start with creating a class in our Model folder. We give the class name as MenuMaster the same as our table name in our Database. In MenuMaster class, we need to create properties like our Table fields as below.

  1. public class MenuMaster  
  2.     {  
  3.         [Key]  
  4.         public int MenuIdentity { get; set; }  
  5.         public string MenuID { get; set; }  
  6.         public string MenuName { get; set; }  
  7.         public string Parent_MenuID { get; set; }  
  8.         public string User_Roll { get; set; }  
  9.         public string MenuFileName { get; set; }  
  10.         public string MenuURL { get; set; }  
  11.         public string USE_YN { get; set; }  
  12.         public DateTime CreatedDate { get; set; }  
  13.     }  

Creating Interface Class

Now, it’s time for us to create an interface with a method named GetMenuMaster() , GetMenuMaster(String UserRole) and we will be implementing this interface in our Service to get all the Menu details from the table and also another method to get menu by user role. For creating the Interface, add a new class to your model folder and name the class as “IMenuMasterService”.

We will change the class to an interface as we are going to create an interface to implement in our service.   

  1. public interface IMenuMasterService  
  2.     {  
  3.         IEnumerable<MenuMaster> GetMenuMaster();  
  4.         IEnumerable<MenuMaster> GetMenuMaster(String UserRole);  
  5.      }  

Creating Service

Now, let’s add a new class Services folder and name the class as “MenuMasterService”. In this class, we will be implementing our interface IMenuMasterService. We know that if we implement the interface, then we should declare the interface method in our class. In this service, we use the interface method and we return the list with Menu details and also return the Menu details by user role. We will be directly Injecting this on our View page.

  1. public class MenuMasterService:IMenuMasterService  
  2.     {  
  3.         private readonly ApplicationDbContext _dbContext;  
  4.   
  5.         public MenuMasterService(ApplicationDbContext dbContext)  
  6.         {  
  7.             _dbContext = dbContext;  
  8.         }  
  9.   
  10.         public IEnumerable<MenuMaster> GetMenuMaster()  
  11.         {   
  12.             return _dbContext.MenuMaster.AsEnumerable();  
  13.   
  14.         }   
  15.   
  16.         public IEnumerable<MenuMaster> GetMenuMaster(string UserRole)  
  17.         {    
  18.             var result = _dbContext.MenuMaster.Where(m => m.User_Roll == UserRole).ToList();    
  19.             return result;  
  20.         }  
  21.     }  

Register the Service

We need to register our created service to the container. Open the Startup.cs from your project to add the service to the container.

In the Startup.cs class, find the method named ConfigureServices and add your service “MenuMasterService” like below. 

services.AddTransient<MenuMasterService, MenuMasterService>();

Inject the Service in the _Layout.cshtml page

Now, it’s much simpler and easier as we can directly inject the service in our View page and bind all the result to our view page. For injecting the Service in our View, here we will be using our existing _Layout.cshtml page. Since we are going to display the menu on top of our website and use it in all our pages, here we have used the _Layout.cshtml page to bind the menu results as menu based on the user who has logged in.

Here first we check if the user is authenticated to our website then if the user is logged in then we get the role details of the logged in user and bind the menu based on the user roles. Here we are binding 2 levels of menu as Main Menu and Submenu. In our table result we check for all the Parenut_MenuID=” *” as we will be displaying the main menu with the parent_MenuID as “*” and in the next inner loop we will display the submenu appropriate to the main menu.

  1. <div class="navbar-collapse collapse">  
  2.  <ul class="nav navbar-nav">  
  3.    <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>  
  4.  @if (User.Identity.IsAuthenticated)  
  5.         {  
  6.             var UserRoles = "";  
  7.             if (@User.IsInRole("Admin"))  
  8.             {  
  9.                 UserRoles = "Admin";  
  10.             }  
  11.             else  
  12.             {  
  13.                 UserRoles = "Manager";  
  14.             }  
  15.   
  16.                 @if (menus.GetMenuMaster(@UserRoles).Any())  
  17.                 {  
  18.                 @if (menus.GetMenuMaster(@UserRoles).Any())  
  19.                 {  
  20.                 @foreach (var menuNames in menus.GetMenuMaster(@UserRoles).Where(n => n.Parent_MenuID == "*"))  
  21.                 {  
  22.                         <li>  
  23.                             <a asp-area="" [email protected] [email protected]>@menuNames.MenuName</a>  
  24.                             <ul class="sub-menu">  
  25.                                 @foreach (var subMenu in menus.GetMenuMaster(@UserRoles).Where(n => n.Parent_MenuID == @menuNames.MenuID))  
  26.                                   {  
  27.                                     <li>  
  28.                                         <a asp-area="" [email protected] [email protected]>@subMenu.MenuName</a>  
  29.                                     </li>  
  30.                                    }  
  31.                             </ul>  
  32.                             </li>  
  33.                 }  
  34.                 }  
  35.                 }  
  36.             }  
  37.   </ul>  

ASP.NET Core

Conclusion

Firstly, create a sample AttendanceDB Database in your SQL Server and run the script to create MenuMaster table and insert sample records. In the appsettings.json  file change the DefaultConnection connection string with your SQL Server Connections. In Startup.cs file add all the code as we discussed in this article. This is a simple demo application and as we have fixed the Admin and Manager roles, you can change as per your requirement. Also the CSS design for menu and sub menu is not good for mobile compatibility so you can add your own bootstrap design to implement your menu style. Hope you like this article and soon I will post  another article with more live examples.