In Focus

ASP.NET Core Razor Pages - Simple Login Using Entity Framework Database First Approach

This article is about creating razor pages base simple asp.net core login application using entity framework.

Microsoft .NET Core framework is more suited for distributed environments rather than single tier machines. Due to this distributed tier nature of the .NET core framework, two major concepts are implanted into the heart of .NET core framework and are utilized quite heavily i.e.:

  1. Service Oriented Architecture as .NET Microservices.
  2. Dependency Injection design pattern.

I will not go into the details about these two concepts. You can consult the provided links for further study and understanding of these two concepts.

In today's tutorial I will demonstrate the creation of a razor pages based simple asp.net core login application using entity framework database first approach.

asp.net core login application

Prerequisites

Following are some prerequisites before you proceed any further in this tutorial:

  1. Basic understanding of ASP.NET Core framework.
  2. Upgrade Windows Power Shell to latest version.
  3. Knowledge about entity framework
  4. Knowledge about Claim Base Identity Model
  5. Knowledge about Bootstrap.
  6. Knowledge about C# programming.
  7. Knowledge about C# LINQ.

You can download the complete source code for this tutorial or you can follow the step by step discussion below. The sample code is developed in Microsoft Visual Studio 2017 Professional & SQL Server 2014.

Let's begin now.

Step 1

First create your existing SQL server database named "db_corelogin" which will be utilized in asp.net core web application by executing following SQL script:

  1. USE [db_corelogin]  
  2. GO  
  3. /****** Object:  StoredProcedure [dbo].[LoginByUsernamePassword]    Script Date: 9/5/2018 8:39:02 PM ******/  
  4. DROP PROCEDURE [dbo].[LoginByUsernamePassword]  
  5. GO  
  6. /****** Object:  Table [dbo].[Login]    Script Date: 9/5/2018 8:39:02 PM ******/  
  7. DROP TABLE [dbo].[Login]  
  8. GO  
  9. /****** Object:  Table [dbo].[Login]    Script Date: 9/5/2018 8:39:02 PM ******/  
  10. SET ANSI_NULLS ON  
  11. GO  
  12. SET QUOTED_IDENTIFIER ON  
  13. GO  
  14. SET ANSI_PADDING ON  
  15. GO  
  16. CREATE TABLE [dbo].[Login](  
  17.  [id] [int] IDENTITY(1,1) NOT NULL,  
  18.  [username] [varchar](50) NOT NULL,  
  19.  [password] [varchar](50) NOT NULL,  
  20.  CONSTRAINT [PK_Login] PRIMARY KEY CLUSTERED   
  21. (  
  22.  [id] ASC  
  23. )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ONON [PRIMARY]  
  24. ON [PRIMARY]  
  25.   
  26. GO  
  27. SET ANSI_PADDING OFF  
  28. GO  
  29. SET IDENTITY_INSERT [dbo].[Login] ON   
  30.   
  31. INSERT [dbo].[Login] ([id], [username], [password]) VALUES (1, N'my-login', N'my-password-123')  
  32. SET IDENTITY_INSERT [dbo].[Login] OFF  
  33. /****** Object:  StoredProcedure [dbo].[LoginByUsernamePassword]    Script Date: 9/5/2018 8:39:02 PM ******/  
  34. SET ANSI_NULLS ON  
  35. GO  
  36. SET QUOTED_IDENTIFIER ON  
  37. GO  
  38. -- =============================================  
  39. -- Author:  <Author,,Asma Khalid>  
  40. -- Create date: <Create Date,,15-Mar-2016>  
  41. -- Description: <Description,,You are Allow to Distribute this Code>  
  42. -- =============================================  
  43. CREATE PROCEDURE [dbo].[LoginByUsernamePassword]   
  44.  @username varchar(50),  
  45.  @password varchar(50)  
  46. AS  
  47. BEGIN  
  48.  SELECT id, username, password  
  49.  FROM Login  
  50.  WHERE username = @username  
  51.  AND password = @password  
  52. END  
  53.   
  54. GO 

In the above script, I have created a login table with existing user login data and also I have created a store procedure to verify the existing login information.

Step 2

Now, create a new .Net core web application project and name it "CoreLoginEfDbFirst" as shown below:

new .Net core web application project

new .Net core web application project

Step 3

Build the solution and ensure that the build is successful then restart Visual Studio.

Step 4

Now, in order to import existing database context object using entity framework to my core web application, I need to install the following library packages via "Tools->NuGet Package Manager->Manage NuGet Packages for Solution" in below-mentioned order:

Manage NuGet Packages

  1. Microsoft.EntityFrameworkCore.SqlServer
  2. Microsoft.EntityFrameworkCore.Tools
  3. Microsoft.EntityFrameworkCore.SqlServer.Design

Manage NuGet Packages



Step 5

Click "Tools->NuGet Package Manager->Package Manager Console" as shown below:

Package Manager Console

Package Manager Console

Package Manager Console

Step 6

Type the following command inside the console as shown below. Do not forget to update your SQL server connection string configuration in this command:

  1. Scaffold-DbContext "Server=SQL SERVER (e.g localhost);Database=DATABASE (e.g db_corelogin);Trusted_Connection=True;user id=SQL USERNAME;password=SQL PASSWORD;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models/DB 

SQL server connection string configuration

The above command will create the following folders and files:

Solution Explorer

Remember that when we use database first approach in asp.net mvc framework, we have a cool graphical designer UI through which we select the tables, store procedures and other database objects to be imported into the web application via entity framework. The database context file also get imported through which we communicate to SQL database engine. In .net core however, there is no cool graphical user interface to import the SQL database context, we have to import the database context via above command and then inside the created .cs database context file, we need to write appropriate business logic methods to access SQL database tables, store procedures or queries to access the data. Login.cs class is the object class of our SQL database table Login.

Step 7

Now, create "Models\DB\LoginByUsernamePassword.cs" file and replace the following code in this file i.e.

  1. using System;  
  2. using System.Collections.Generic;  
  3.   
  4. namespace CoreLoginEfDbFirst.Models.DB  
  5. {  
  6.     public partial class LoginByUsernamePassword  
  7.     {  
  8.         public int Id { getset; }  
  9.         public string Username { getset; }  
  10.         public string Password { getset; }  
  11.     }  

In the above code, I have created an object class for my store procedure returning data.

Step 8

Now, create "Models\LoginViewModel.cs" file and replace the following code in it i.e.

  1. //-----------------------------------------------------------------------  
  2. // <copyright file="LoginViewModel.cs" company="None">  
  3. //     Copyright (c) Allow to distribute this code and utilize this code for personal or commercial purpose.  
  4. // </copyright>  
  5. // <author>Asma Khalid</author>  
  6. //-----------------------------------------------------------------------  
  7.   
  8. namespace CoreLoginEfDbFirst.Models  
  9. {  
  10.     using System.Collections.Generic;  
  11.     using System.ComponentModel.DataAnnotations;  
  12.   
  13.     /// <summary>  
  14.     /// Login view model class.  
  15.     /// </summary>  
  16.     public class LoginViewModel  
  17.     {  
  18.         #region Properties  
  19.   
  20.         /// <summary>  
  21.         /// Gets or sets to username address.  
  22.         /// </summary>  
  23.         [Required]  
  24.         [Display(Name = "Username")]  
  25.         public string Username { getset; }  
  26.   
  27.         /// <summary>  
  28.         /// Gets or sets to password address.  
  29.         /// </summary>  
  30.         [Required]  
  31.         [DataType(DataType.Password)]  
  32.         [Display(Name = "Password")]  
  33.         public string Password { getset; }  
  34.  
  35.         #endregion  
  36.     }  

The above piece of code is my view model class which will be attached to the target view in order to process user inputs.

Step 9

Now, open "Models\DB\db_coreloginContext.cs"file and replace the following code in it:

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Data.SqlClient;  
  4. using System.Threading.Tasks;  
  5. using Microsoft.EntityFrameworkCore;  
  6. using Microsoft.EntityFrameworkCore.Metadata;  
  7.   
  8. namespace CoreLoginEfDbFirst.Models.DB  
  9. {  
  10.     public partial class db_coreloginContext : DbContext  
  11.     {  
  12.         public db_coreloginContext()  
  13.         {  
  14.         }  
  15.   
  16.         public db_coreloginContext(DbContextOptions<db_coreloginContext> options)  
  17.             : base(options)  
  18.         {  
  19.         }  
  20.   
  21.         public virtual DbSet<Login> Login { getset; }  
  22.   
  23.         ////protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)  
  24.         ////{  
  25.         ////    if (!optionsBuilder.IsConfigured)  
  26.         ////    {  
  27.         ////        optionsBuilder.UseSqlServer("Server=SQL SERVER;Database=DATABASE;User id=SQL USERNAME;Password=SQL PASSWORD;Trusted_Connection=True;");  
  28.         ////    }  
  29.         ////}  
  30.   
  31.         protected override void OnModelCreating(ModelBuilder modelBuilder)  
  32.         {  
  33.             ////modelBuilder.Entity<Login>(entity =>  
  34.             ////{  
  35.             ////    entity.Property(e => e.Id).HasColumnName("id");  
  36.   
  37.             ////    entity.Property(e => e.Password)  
  38.             ////        .IsRequired()  
  39.             ////        .HasColumnName("password")  
  40.             ////        .HasMaxLength(50)  
  41.             ////        .IsUnicode(false);  
  42.   
  43.             ////    entity.Property(e => e.Username)  
  44.             ////        .IsRequired()  
  45.             ////        .HasColumnName("username")  
  46.             ////        .HasMaxLength(50)  
  47.             ////        .IsUnicode(false);  
  48.             ////});  
  49.   
  50.             // [Asma Khalid]: Query for store procedure.  
  51.             modelBuilder.Query<LoginByUsernamePassword>();  
  52.         }  
  53.  
  54.         #region Login by username and password store procedure method.  
  55.   
  56.         /// <summary>  
  57.         /// Login by username and password store procedure method.  
  58.         /// </summary>  
  59.         /// <param name="usernameVal">Username value parameter</param>  
  60.         /// <param name="passwordVal">Password value parameter</param>  
  61.         /// <returns>Returns - List of logins by username and password</returns>  
  62.         public async Task<List<LoginByUsernamePassword>> LoginByUsernamePasswordMethodAsync(string usernameVal, string passwordVal)  
  63.         {  
  64.             // Initialization.  
  65.             List<LoginByUsernamePassword> lst = new List<LoginByUsernamePassword>();  
  66.   
  67.             try  
  68.             {  
  69.                 // Settings.  
  70.                 SqlParameter usernameParam = new SqlParameter("@username", usernameVal ?? (object)DBNull.Value);  
  71.                 SqlParameter passwordParam = new SqlParameter("@password", passwordVal ?? (object)DBNull.Value);  
  72.   
  73.                 // Processing.  
  74.                 string sqlQuery = "EXEC [dbo].[LoginByUsernamePassword] " +  
  75.                                     "@username, @password";  
  76.   
  77.                 lst = await this.Query<LoginByUsernamePassword>().FromSql(sqlQuery, usernameParam, passwordParam).ToListAsync();  
  78.             }  
  79.             catch (Exception ex)  
  80.             {  
  81.                 throw ex;  
  82.             }  
  83.   
  84.             // Info.  
  85.             return lst;  
  86.         }  
  87.  
  88.         #endregion  
  89.     }  

As I have already mentioned, .Net core heavily utilizes "Service Oriented Architecture as .NET Microservices and Dependency Injection design pattern" concepts. Therefore, we need to make some changes in the above auto generated database context class, otherwise we will not be able to communicate with SQL server database engine. So, the above class ensures that "OnConfiguring(...)" is either removed or commented out as I have already commented out this method in "db_coreloginContext.cs" file. Ensure that your database context file above must also have overload constructor as shown below:

  1. public db_coreloginContext(DbContextOptions<db_coreloginContext> options)  
  2.     : base(options)  
  3. {  

Rest is your choice i.e what new database logic methods you need to add or remove any existing entities. In order to access data objects from SQL database via custom queries, store procedures or direct query from tables, you need to register your target custom objects with the model builder inside your "OnModelCreating(...)" method of "db_coreloginContext.cs" database context class. As shown below I will not do any direct table query so, I have commented out my table entity object Login pre-build registration with the model builder and since, I am using the result of my SQL server database store procedure, therefore, I have registered my custom store procedure data returining object with the model builder:

  1. protected override void OnModelCreating(ModelBuilder modelBuilder)  
  2. {  
  3.     ////modelBuilder.Entity<Login>(entity =>  
  4.     ////{  
  5.     ////    entity.Property(e => e.Id).HasColumnName("id");  
  6.   
  7.     ////    entity.Property(e => e.Password)  
  8.     ////        .IsRequired()  
  9.     ////        .HasColumnName("password")  
  10.     ////        .HasMaxLength(50)  
  11.     ////        .IsUnicode(false);  
  12.   
  13.     ////    entity.Property(e => e.Username)  
  14.     ////        .IsRequired()  
  15.     ////        .HasColumnName("username")  
  16.     ////        .HasMaxLength(50)  
  17.     ////        .IsUnicode(false);  
  18.     ////});  
  19.   
  20.     // [Asma Khalid]: Query for store procedure.  
  21.     modelBuilder.Query<LoginByUsernamePassword>();  

Then, I have created "LoginByUsernamePasswordMethodAsync(...)" method, which has access to my store procedure inside SQL server database:

  1. #region Login by username and password store procedure method.  
  2.   
  3.         /// <summary>  
  4.         /// Login by username and password store procedure method.  
  5.         /// </summary>  
  6.         /// <param name="usernameVal">Username value parameter</param>  
  7.         /// <param name="passwordVal">Password value parameter</param>  
  8.         /// <returns>Returns - List of logins by username and password</returns>  
  9.         public async Task<List<LoginByUsernamePassword>> LoginByUsernamePasswordMethodAsync(string usernameVal, string passwordVal)  
  10.         {  
  11.             // Initialization.  
  12.             List<LoginByUsernamePassword> lst = new List<LoginByUsernamePassword>();  
  13.   
  14.             try  
  15.             {  
  16.                 // Settings.  
  17.                 SqlParameter usernameParam = new SqlParameter("@username", usernameVal ?? (object)DBNull.Value);  
  18.                 SqlParameter passwordParam = new SqlParameter("@password", passwordVal ?? (object)DBNull.Value);  
  19.   
  20.                 // Processing.  
  21.                 string sqlQuery = "EXEC [dbo].[LoginByUsernamePassword] " +  
  22.                                     "@username, @password";  
  23.   
  24.                 lst = await this.Query<LoginByUsernamePassword>().FromSql(sqlQuery, usernameParam, passwordParam).ToListAsync();  
  25.             }  
  26.             catch (Exception ex)  
  27.             {  
  28.                 throw ex;  
  29.             }  
  30.   
  31.             // Info.  
  32.             return lst;  
  33.         }  
  34.  
  35.         #endregion 

Step 10

In order to access SQL server database I need to store my database connection string in "appsettings.json" file which is the recommended way. So, open "appsettings.json" file and replace the following code in it i.e.

  1. {  
  2.   "ConnectionStrings": {  
  3.     "db_corelogin""Server=SQL SERVER;Database=DATABASE;Trusted_Connection=True;user id=SQL USERNAME;password=SQL PASSWORD;"  
  4.   },  
  5.   "Logging": {  
  6.     "IncludeScopes"false,  
  7.     "LogLevel": {  
  8.       "Default""Warning"  
  9.     }  
  10.   }  

Do not forget to update your SQL server configurations in the above connection string.

Step 11

Now, I need to register my database context as .NET Microservices with the .net core framework in order to access my database within my application. To do so, open the "Startup.cs" file and add following line of code at the end of "ConfigureServices(...)" method i.e.

  1. // [Asma Khalid]: Register SQL database configuration context as services.    
  2. services.AddDbContext<db_coreloginContext>(options => options.UseSqlServer(Configuration.GetConnectionString("db_corelogin"))); 

I have now registered my database context as .NET Microservices with the .net core framework using my SQL server connection string configuration. So, now I can easily access my database context object using Dependency Injection design pattern. Just for your knowledge dependency injection design pattern enables access to the target global object via overload constructor parameter of the class that requires the access. When I register my database context as the above line of code, the framework will automatically instantiate my database context object at global level and when my any target class requires access to the database context object then that class needs to utilize "db_coreloginContext" database context object as a parameter of its overload constructor, that's the beauty of dependency injection design pattern.

Step 12

Do a little cleanup of your folder hierarchy i.e.

  1. Remove "Pages\_Layout.cshtml" file.
  2. Move "Pages\_ValidationScriptsPartial.cshtml" file to Views\Shared folder.
  3. Except "Pages\_ViewImports.cshtml" file, "Pages\_ViewStart.cshtml" file and "Pages\Error.cshtml" file, remove all other files inside "Pages" folder.

Step 13

Create, "Views\Shared\_Layout.cshtml" file and replace the following code in it i.e.

  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4.     <meta charset="utf-8" />  
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0" />  
  6.     <title>@ViewBag.Title</title>  
  7.   
  8.     <environment include="Development">  
  9.         <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />  
  10.         <link rel="stylesheet" href="~/css/site.css" />  
  11.     </environment>  
  12.     <environment exclude="Development">  
  13.         <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"  
  14.               asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"  
  15.               asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />  
  16.         <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />  
  17.     </environment>  
  18. </head>  
  19. <body>  
  20.     <nav class="navbar navbar-inverse navbar-fixed-top">  
  21.         <div class="container">  
  22.             <div class="navbar-header">  
  23.                 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">  
  24.                     <span class="sr-only">Toggle navigation</span>  
  25.                     <span class="icon-bar"></span>  
  26.                     <span class="icon-bar"></span>  
  27.                     <span class="icon-bar"></span>  
  28.                 </button>  
  29.   
  30.             </div>  
  31.             <div class="navbar-collapse collapse">  
  32.             </div>  
  33.         </div>  
  34.     </nav>  
  35.     <div class="container body-content">  
  36.         @RenderBody()  
  37.         <hr />  
  38.         <footer>  
  39.             <center>  
  40.                 <p><strong>Copyright © @DateTime.Now.Year - <a href="http://wwww.asmak9.com/">Asma's Blog</a>.</strong> All rights reserved.</p>  
  41.             </center>  
  42.         </footer>  
  43.     </div>  
  44.   
  45.     <environment include="Development">  
  46.         <script src="~/lib/jquery/dist/jquery.js"></script>  
  47.         <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>  
  48.         <script src="~/js/site.js" asp-append-version="true"></script>  
  49.     </environment>  
  50.     <environment exclude="Development">  
  51.         <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"  
  52.                 asp-fallback-src="~/lib/jquery/dist/jquery.min.js"  
  53.                 asp-fallback-test="window.jQuery"  
  54.                 crossorigin="anonymous"  
  55.                 integrity="sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk">  
  56.         </script>  
  57.         <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js"  
  58.                 asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"  
  59.                 asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"  
  60.                 crossorigin="anonymous"  
  61.                 integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa">  
  62.         </script>  
  63.         <script src="~/js/site.min.js" asp-append-version="true"></script>  
  64.     </environment>  
  65.   
  66.     @RenderSection("Scripts", required: false)  
  67. </body>  
  68. </html> 

In the above code, I have created a basic layout view of my web application.

Step 14

Now, create "Pages\Index.cshtml" file with model and replace the following code in it i.e.

  1. @page "{handler?}"  
  2.   
  3. @model IndexModel  
  4.   
  5. @{  
  6.     ViewBag.Title = "ASP.NET Core Razor Pages - Simple Login Database First Approach";  
  7. }  
  8.   
  9. <h2>@ViewBag.Title.</h2>  
  10.   
  11. <div class="row">  
  12.     <div class="col-md-8">  
  13.         <section>  
  14.             <form method="post" role="form" class="form-horizontal">  
  15.                 @Html.AntiForgeryToken()  
  16.   
  17.                 <h4>Use a local account to log in.</h4>  
  18.                 <hr />  
  19.   
  20.                 @Html.ValidationSummary(true, "", new { @class = "text-danger" })  
  21.   
  22.                 <div class="form-group">  
  23.                     @Html.LabelFor(m => m.LoginModel.Username, new { @class = "col-md-2 control-label" })  
  24.                     <div class="col-md-10">  
  25.                         @Html.TextBoxFor(m => m.LoginModel.Username, new { @class = "form-control" })  
  26.                         @Html.ValidationMessageFor(m => m.LoginModel.Username, "", new { @class = "text-danger" })  
  27.                     </div>  
  28.                 </div>  
  29.   
  30.                 <div class="form-group">  
  31.                     @Html.LabelFor(m => m.LoginModel.Password, new { @class = "col-md-2 control-label" })  
  32.                     <div class="col-md-10">  
  33.                         @Html.PasswordFor(m => m.LoginModel.Password, new { @class = "form-control" })  
  34.                         @Html.ValidationMessageFor(m => m.LoginModel.Password, "", new { @class = "text-danger" })  
  35.                     </div>  
  36.                 </div>  
  37.   
  38.                 @*<div class="form-group">  
  39.                         <label asp-for="LoginModel.Username" class = "col-md-2 control-label"></label>  
  40.                         <div class="col-md-10">  
  41.                             <input asp-for="LoginModel.Username" class="form-control" />  
  42.                             <span class="alert-danger" asp-validation-for="LoginModel.Username"></span>  
  43.                         </div>  
  44.                     </div>  
  45.   
  46.                     <div class="form-group">  
  47.                         <label asp-for="LoginModel.Password" class="col-md-2 control-label"></label>  
  48.                         <div class="col-md-10">  
  49.                             <input asp-for="LoginModel.Password" class="form-control" />  
  50.                             <span class="alert-danger" asp-validation-for="LoginModel.Password"></span>  
  51.                         </div>  
  52.                     </div>*@  
  53.   
  54.                 <div class="form-group">  
  55.                     <div class="col-md-offset-2 col-md-10">  
  56.                         <button asp-page-handler="LogIn" class="btn btn-default">Log in</button>  
  57.                     </div>  
  58.                 </div>  
  59.             </form>  
  60.         </section>  
  61.     </div>  
  62. </div> 

In the above code, I have created a simple login form for the razor page with the combination of razor auto generated code as similar to asp.net mvc framework and asp- attributes within the HTML tags as prescribed for razor pages.

Step 15

Open, "Pages\Index.cshtml.cs" file and replace following code in it i.e.

  1. //-----------------------------------------------------------------------  
  2. // <copyright file="Index.cshtml.cs" company="None">  
  3. //     Copyright (c) Allow to distribute this code and utilize this code for personal or commercial purpose.  
  4. // </copyright>  
  5. // <author>Asma Khalid</author>  
  6. //-----------------------------------------------------------------------  
  7.   
  8. namespace CoreLoginEfDbFirst.Pages  
  9. {  
  10.     using System;  
  11.     using System.Collections.Generic;  
  12.     using System.Linq;  
  13.     using System.Security.Claims;  
  14.     using System.Threading.Tasks;  
  15.     using CoreLoginEfDbFirst.Models;  
  16.     using CoreLoginEfDbFirst.Models.DB;  
  17.     using Microsoft.AspNetCore.Authentication;  
  18.     using Microsoft.AspNetCore.Authentication.Cookies;  
  19.     using Microsoft.AspNetCore.Mvc;  
  20.     using Microsoft.AspNetCore.Mvc.RazorPages;  
  21.   
  22.     /// <summary>  
  23.     /// Index page model class.  
  24.     /// </summary>  
  25.     public class IndexModel : PageModel  
  26.     {              
  27.         #region Private Properties.  
  28.   
  29.         /// <summary>  
  30.         /// Database Manager property.  
  31.         /// </summary>  
  32.         private readonly db_coreloginContext databaseManager;  
  33.  
  34.         #endregion  
  35.  
  36.         #region Default Constructor method.  
  37.   
  38.         /// <summary>  
  39.         /// Initializes a new instance of the <see cref="IndexModel"/> class.  
  40.         /// </summary>  
  41.         /// <param name="databaseManagerContext">Database manager context parameter</param>  
  42.         public IndexModel(db_coreloginContext databaseManagerContext)  
  43.         {  
  44.             try  
  45.             {  
  46.                 // Settings.  
  47.                 this.databaseManager = databaseManagerContext;  
  48.             }  
  49.             catch (Exception ex)  
  50.             {  
  51.                 // Info  
  52.                 Console.Write(ex);  
  53.             }  
  54.         }  
  55.  
  56.         #endregion  
  57.  
  58.         #region Public Properties  
  59.   
  60.         /// <summary>  
  61.         /// Gets or sets login model property.  
  62.         /// </summary>  
  63.         [BindProperty]  
  64.         public LoginViewModel LoginModel { getset; }  
  65.  
  66.         #endregion  
  67.          
  68.         #region On Get method.  
  69.   
  70.         /// <summary>  
  71.         /// GET: /Index  
  72.         /// </summary>  
  73.         /// <returns>Returns - Appropriate page </returns>  
  74.         public IActionResult OnGet()  
  75.         {  
  76.             try  
  77.             {  
  78.                 // Verification.  
  79.                 if (this.User.Identity.IsAuthenticated)  
  80.                 {  
  81.                     // Home Page.  
  82.                     return this.RedirectToPage("/Home/Index");  
  83.                 }  
  84.             }  
  85.             catch (Exception ex)  
  86.             {  
  87.                 // Info  
  88.                 Console.Write(ex);  
  89.             }  
  90.   
  91.             // Info.  
  92.             return this.Page();  
  93.         }  
  94.  
  95.         #endregion  
  96.  
  97.         #region On Post Login method.  
  98.   
  99.         /// <summary>  
  100.         /// POST: /Index/LogIn  
  101.         /// </summary>  
  102.         /// <returns>Returns - Appropriate page </returns>  
  103.         public async Task<IActionResult> OnPostLogIn()  
  104.         {  
  105.             try  
  106.             {  
  107.                 // Verification.  
  108.                 if (ModelState.IsValid)  
  109.                 {  
  110.                     // Initialization.  
  111.                     var loginInfo = await this.databaseManager.LoginByUsernamePasswordMethodAsync(this.LoginModel.Username, this.LoginModel.Password);  
  112.   
  113.                     // Verification.  
  114.                     if (loginInfo != null && loginInfo.Count() > 0)  
  115.                     {  
  116.                         // Initialization.  
  117.                         var logindetails = loginInfo.First();  
  118.   
  119.                         // Login In.  
  120.                         await this.SignInUser(logindetails.Username, false);  
  121.   
  122.                         // Info.  
  123.                         return this.RedirectToPage("/Home/Index");  
  124.                     }  
  125.                     else  
  126.                     {  
  127.                         // Setting.  
  128.                         ModelState.AddModelError(string.Empty, "Invalid username or password.");  
  129.                     }  
  130.                 }  
  131.             }  
  132.             catch (Exception ex)  
  133.             {  
  134.                 // Info  
  135.                 Console.Write(ex);  
  136.             }  
  137.   
  138.             // Info.  
  139.             return this.Page();  
  140.         }  
  141.  
  142.         #endregion  
  143.          
  144.         #region Helpers  
  145.  
  146.         #region Sign In method.  
  147.   
  148.         /// <summary>  
  149.         /// Sign In User method.  
  150.         /// </summary>  
  151.         /// <param name="username">Username parameter.</param>  
  152.         /// <param name="isPersistent">Is persistent parameter.</param>  
  153.         /// <returns>Returns - await task</returns>  
  154.         private async Task SignInUser(string username, bool isPersistent)  
  155.         {  
  156.             // Initialization.  
  157.             var claims = new List<Claim>();  
  158.   
  159.             try  
  160.             {  
  161.                 // Setting  
  162.                 claims.Add(new Claim(ClaimTypes.Name, username));  
  163.                 var claimIdenties = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);  
  164.                 var claimPrincipal = new ClaimsPrincipal(claimIdenties);  
  165.                 var authenticationManager = Request.HttpContext;  
  166.   
  167.                 // Sign In.  
  168.                 await authenticationManager.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimPrincipal, new AuthenticationProperties() { IsPersistent = isPersistent });  
  169.             }  
  170.             catch (Exception ex)  
  171.             {  
  172.                 // Info  
  173.                 throw ex;  
  174.             }  
  175.         }  
  176.  
  177.         #endregion  
  178.  
  179.         #endregion  
  180.     }  

Let's diagnose the above code chunk by chunk:

  1. #region Private Properties.  
  2.   
  3. /// <summary>  
  4. /// Database Manager property.  
  5. /// </summary>  
  6. private readonly db_coreloginContext databaseManager; 

The above piece of code is basically a read only property to store the reference of my database context object as a part or dependency injection design pattern. Then in the below code I have created an overload constructor with the database context object as a parameter following dependency injection design pattern and within my overload constructor I have stored the reference of the database context object for my class i.e.

  1. #region Default Constructor method.  
  2.   
  3.         /// <summary>  
  4.         /// Initializes a new instance of the <see cref="IndexModel"/> class.  
  5.         /// </summary>  
  6.         /// <param name="databaseManagerContext">Database manager context parameter</param>  
  7.         public IndexModel(db_coreloginContext databaseManagerContext)  
  8.         {  
  9.             try  
  10.             {  
  11.                 // Settings.  
  12.                 this.databaseManager = databaseManagerContext;  
  13.             }  
  14.             catch (Exception ex)  
  15.             {  
  16.                 // Info  
  17.                 Console.Write(ex);  
  18.             }  
  19.         }  
  20.  
  21.         #endregion 

In asp.net core framework unlike asp.net mvc framework, in order to bind the view model class to the UI I need to define "[BindProperty]" data annotation above the view model public property which is defined within the IndexModel class "Pages\Index.cshtml.cs" file. as shown below i.e.

  1. #region Public Properties  
  2.   
  3.         /// <summary>  
  4.         /// Gets or sets login model property.  
  5.         /// </summary>  
  6.         [BindProperty]  
  7.         public LoginViewModel LoginModel { getset; }  
  8.  
  9.         #endregion 

Now, below "OnGet()" method is the default mandatory method which will be executed when the page is called. I have simply verified the user authorization in the "OnGet()" method i.e.; if user is logged in then go to home page, otherwise go to login page i.e.:

  1. #region On Get method.  
  2.   
  3.         /// <summary>  
  4.         /// GET: /Index  
  5.         /// </summary>  
  6.         /// <returns>Returns - Appropriate page </returns>  
  7.         public IActionResult OnGet()  
  8.         {  
  9.             try  
  10.             {  
  11.                 // Verification.  
  12.                 if (this.User.Identity.IsAuthenticated)  
  13.                 {  
  14.                     // Home Page.  
  15.                     return this.RedirectToPage("/Home/Index");  
  16.                 }  
  17.             }  
  18.             catch (Exception ex)  
  19.             {  
  20.                 // Info  
  21.                 Console.Write(ex);  
  22.             }  
  23.   
  24.             // Info.  
  25.             return this.Page();  
  26.         }  
  27.  
  28.         #endregion 

Next, I have created the "OnPostLogIn(..)" method which will verify the username and password via SQL database access and then by using claim based identity model mechanism sign the user into my web application. Here, notice that in order to tell the framework that the method is posted, I need to write the prefix "OnPost" before writing the handler name of my choice; i.e. LogIn . So, it will become "OnPostLogin()" method, this naming convention is important. A helper "SignInUser(...)" method is also written for signing in the user via claim based identity model mechanism i.e.:

  1. #region On Post Login method.  
  2.   
  3. /// <summary>  
  4. /// POST: /Index/LogIn  
  5. /// </summary>  
  6. /// <returns>Returns - Appropriate page </returns>  
  7. public async Task<IActionResult> OnPostLogIn()  
  8. {  
  9.     try  
  10.     {  
  11.         // Verification.  
  12.         if (ModelState.IsValid)  
  13.         {  
  14.             // Initialization.  
  15.             var loginInfo = await this.databaseManager.LoginByUsernamePasswordMethodAsync(this.LoginModel.Username, this.LoginModel.Password);  
  16.   
  17.             // Verification.  
  18.             if (loginInfo != null && loginInfo.Count() > 0)  
  19.             {  
  20.                 // Initialization.  
  21.                 var logindetails = loginInfo.First();  
  22.   
  23.                 // Login In.  
  24.                 await this.SignInUser(logindetails.Username, false);  
  25.   
  26.                 // Info.  
  27.                 return this.RedirectToPage("/Home/Index");  
  28.             }  
  29.             else  
  30.             {  
  31.                 // Setting.  
  32.                 ModelState.AddModelError(string.Empty, "Invalid username or password.");  
  33.             }  
  34.         }  
  35.     }  
  36.     catch (Exception ex)  
  37.     {  
  38.         // Info  
  39.         Console.Write(ex);  
  40.     }  
  41.   
  42.     // Info.  
  43.     return this.Page();  
  44. }  
  45.  
  46. #endregion  
  47.  
  48. #region Helpers  
  49.  
  50. #region Sign In method.  
  51.   
  52. /// <summary>  
  53. /// Sign In User method.  
  54. /// </summary>  
  55. /// <param name="username">Username parameter.</param>  
  56. /// <param name="isPersistent">Is persistent parameter.</param>  
  57. /// <returns>Returns - await task</returns>  
  58. private async Task SignInUser(string username, bool isPersistent)  
  59. {  
  60.     // Initialization.  
  61.     var claims = new List<Claim>();  
  62.   
  63.     try  
  64.     {  
  65.         // Setting  
  66.         claims.Add(new Claim(ClaimTypes.Name, username));  
  67.         var claimIdenties = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);  
  68.         var claimPrincipal = new ClaimsPrincipal(claimIdenties);  
  69.         var authenticationManager = Request.HttpContext;  
  70.   
  71.         // Sign In.  
  72.         await authenticationManager.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimPrincipal, new AuthenticationProperties() { IsPersistent = isPersistent });  
  73.     }  
  74.     catch (Exception ex)  
  75.     {  
  76.         // Info  
  77.         throw ex;  
  78.     }  
  79. }  
  80.  
  81. #endregion  
  82.  
  83. #endregion 

To access the "OnPostLogIn(...)" method in the HTML, you do not need to write the "OnPost" prefix, but, only "LogIn" as shown in the below line of code from "Pages\Index.cshtml" file which will post request to the "OnPostLogIn(...)" method i.e.

  1. <div class="form-group">  
  2.     <div class="col-md-offset-2 col-md-10">  
  3.         <button asp-page-handler="LogIn" class="btn btn-default">Log in</button>  
  4.     </div>  
  5. </div> 

Step 16

Open, "Startup.cs" file to register the authorization services with the .net core framework in order to utilize the authentication services of the claim based identity model mechanism. Go to, "ConfigureServices(...)" method and add following lines of code before the database context registration i.e.:

  1. // This method gets called by the runtime. Use this method to add services to the container.  
  2. public void ConfigureServices(IServiceCollection services)  
  3. {  
  4.     // [Asma Khalid]: Authorization settings.  
  5.     services.AddAuthentication(options =>  
  6.     {  
  7.         options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;  
  8.         options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;  
  9.         options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;  
  10.     }).AddCookie(options =>  
  11.     {  
  12.         options.LoginPath = new PathString("/Index");  
  13.         options.ExpireTimeSpan = TimeSpan.FromMinutes(5.0);  
  14.     });  
  15.   
  16.     // [Asma Khalid]: Authorization settings.  
  17.     services.AddMvc().AddRazorPagesOptions(options =>  
  18.     {  
  19.         options.Conventions.AuthorizeFolder("/");  
  20.         options.Conventions.AllowAnonymousToPage("/Index");  
  21.     });  
  22.   
  23.     // [Asma Khalid]: Register SQL database configuration context as services.    
  24.     services.AddDbContext<db_coreloginContext>(options => options.UseSqlServer(Configuration.GetConnectionString("db_corelogin")));  

Step 17

In "Startup.cs" file for authenticaon purposes add the following lines of code at the end of "Configure(...)" method i.e.:

  1. // [Asma Khalid]: Register simple authorization.  
  2. app.UseAuthentication(); 

The final "Startup.cs" file will look as shown below, i.e.:

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Threading.Tasks;  
  5. using CoreLoginEfDbFirst.Models.DB;  
  6. using Microsoft.AspNetCore.Authentication.Cookies;  
  7. using Microsoft.AspNetCore.Builder;  
  8. using Microsoft.AspNetCore.Hosting;  
  9. using Microsoft.AspNetCore.Http;  
  10. using Microsoft.EntityFrameworkCore;  
  11. using Microsoft.Extensions.Configuration;  
  12. using Microsoft.Extensions.DependencyInjection;  
  13.   
  14. namespace CoreLoginEfDbFirst  
  15. {  
  16.     public class Startup  
  17.     {  
  18.         public Startup(IConfiguration configuration)  
  19.         {  
  20.             Configuration = configuration;  
  21.         }  
  22.   
  23.         public IConfiguration Configuration { get; }  
  24.   
  25.         // This method gets called by the runtime. Use this method to add services to the container.  
  26.         public void ConfigureServices(IServiceCollection services)  
  27.         {  
  28.             // [Asma Khalid]: Authorization settings.  
  29.             services.AddAuthentication(options =>  
  30.             {  
  31.                 options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;  
  32.                 options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;  
  33.                 options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;  
  34.             }).AddCookie(options =>  
  35.             {  
  36.                 options.LoginPath = new PathString("/Index");  
  37.                 options.ExpireTimeSpan = TimeSpan.FromMinutes(5.0);  
  38.             });  
  39.   
  40.             // [Asma Khalid]: Authorization settings.  
  41.             services.AddMvc().AddRazorPagesOptions(options =>  
  42.             {  
  43.                 options.Conventions.AuthorizeFolder("/");  
  44.                 options.Conventions.AllowAnonymousToPage("/Index");  
  45.             });  
  46.   
  47.             // [Asma Khalid]: Register SQL database configuration context as services.    
  48.             services.AddDbContext<db_coreloginContext>(options => options.UseSqlServer(Configuration.GetConnectionString("db_corelogin")));  
  49.         }  
  50.   
  51.         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.  
  52.         public void Configure(IApplicationBuilder app, IHostingEnvironment env)  
  53.         {  
  54.             if (env.IsDevelopment())  
  55.             {  
  56.                 app.UseBrowserLink();  
  57.                 app.UseDeveloperExceptionPage();  
  58.             }  
  59.             else  
  60.             {  
  61.                 app.UseExceptionHandler("/Error");  
  62.             }  
  63.   
  64.             app.UseStaticFiles();  
  65.   
  66.             // [Asma Khalid]: Register simple authorization.  
  67.             app.UseAuthentication();  
  68.   
  69.             app.UseMvc();  
  70.         }  
  71.     }  

Step 18

Create "Pages\Home\Index.cshtml" file and replace the following code in it i.e.:

  1. @page   
  2.   
  3.   
  4. @model CoreLoginEfDbFirst.Pages.Home.IndexModel  
  5.   
  6. @{  
  7.     ViewBag.Title = "ASP.NET Core Razor Pages - Simple Login Database First Approach";  
  8. }  
  9.   
  10. <h2>@ViewBag.Title.</h2>  
  11.   
  12. <div class="jumbotron">  
  13.     <h1>Welcome</h1>  
  14.     <p class="lead">Login from "@User.Identity.Name" Account.</p>  
  15. </div>  
  16.   
  17. <div class="row">  
  18.     <div class="col-md-8">  
  19.         <form method="post" role="form" class="form-horizontal">  
  20.             <div class="form-group">  
  21.                 <div class="col-md-offset-2 col-md-10">  
  22.                     <button asp-page-handler="LogOff" class="btn btn-default">Log Off</button>  
  23.                 </div>  
  24.             </div>  
  25.         </form>  
  26.     </div>  
  27. </div> 

In the above code, I have created a simple home page which is accessible to only authorized users of this application.

Step 19

Now, open "Pages\Home\Index.cshtml.cs" file and replace the following in it i.e.:

  1. //-----------------------------------------------------------------------  
  2. // <copyright file="Index.cshtml.cs" company="None">  
  3. //     Copyright (c) Allow to distribute this code and utilize this code for personal or commercial purpose.  
  4. // </copyright>  
  5. // <author>Asma Khalid</author>  
  6. //-----------------------------------------------------------------------  
  7.   
  8. namespace CoreLoginEfDbFirst.Pages.Home  
  9. {  
  10.     using System;  
  11.     using System.Collections.Generic;  
  12.     using System.Linq;  
  13.     using System.Threading.Tasks;  
  14.     using Microsoft.AspNetCore.Authentication;  
  15.     using Microsoft.AspNetCore.Authentication.Cookies;  
  16.     using Microsoft.AspNetCore.Authorization;  
  17.     using Microsoft.AspNetCore.Mvc;  
  18.     using Microsoft.AspNetCore.Mvc.RazorPages;  
  19.   
  20.     /// <summary>  
  21.     /// Index page model class.  
  22.     /// </summary>  
  23.     [Authorize]  
  24.     public class IndexModel : PageModel  
  25.     {  
  26.         #region On Get method.  
  27.   
  28.         /// <summary>  
  29.         /// On Get method.  
  30.         /// </summary>  
  31.         public void OnGet()  
  32.         {  
  33.             try  
  34.             {  
  35.             }  
  36.             catch (Exception ex)  
  37.             {  
  38.                 // Info  
  39.                 Console.Write(ex);  
  40.             }  
  41.         }  
  42.  
  43.         #endregion  
  44.  
  45.         #region Log Out method.  
  46.   
  47.         /// <summary>  
  48.         /// POST: /Home/Index/LogOff  
  49.         /// </summary>  
  50.         /// <returns>Return log off action</returns>  
  51.         public async Task<IActionResult> OnPostLogOff()  
  52.         {  
  53.             try  
  54.             {  
  55.                 // Setting.  
  56.                 var authenticationManager = Request.HttpContext;  
  57.   
  58.                 // Sign Out.  
  59.                 await authenticationManager.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);  
  60.             }  
  61.             catch (Exception ex)  
  62.             {  
  63.                 // Info  
  64.                 throw ex;  
  65.             }  
  66.   
  67.             // Info.  
  68.             return this.RedirectToPage("/Index");  
  69.         }  
  70.  
  71.         #endregion  
  72.     }  

In the above code, I have simply created a default "OnGet()" method and "OnPostLogOff(...)" method. I am also using "[Authorize]" data annotation on this class in order to prevent unauthorized access to this application.

Step 20

Execute the project and you will be able to see the following i.e.:

Simple Login using Entity Framework Database First Approach

Simple Login using Entity Framework Database First Approach

Simple Login using Entity Framework Database First Approach

Conclusion

In this article, you learned to create razor pages based simple asp.net core login application using entity framework. You learned to scaffold existing database context into your web application via NuGet Package Console command. You also learned about dependency injection design pattern. You also learned about registration of database context .net microservices with the .net core framework in order to utilize the SQL server database access. You also learned about claim based identity model authorization with a .net core web application. You also learned about razor pages development and you also learned about accessing store procedures with .net core entity framework web application.