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

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.

The example 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. Example database scripts named "db_corelogin" is provided with the downloadable solution which will be utilized in asp.net core web application.
Make sure to update SQL server connection string in the provided example solution.

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" object class file to access your store procedure results.

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 { get; set; }  
  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 { get; set; }  
  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 add following lines of code in it i.e.

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

Now, create "Pages\Index.cshtml" page file with view model attached to it.

Step 14

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

  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 { get; set; }  
  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.  

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 15

Open, "Startup.cs" file to register the authorization services with the .net core framework in order to utilize the authentication services of the claim base 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 16

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();
Step 17


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.