ProudMonkey

Building Web Application using Entity Framework and MVC 5: Part 1

Introduction

Technologies are constantly evolving and as developer we need to cope up with what’s the latest or at least popular nowadays. As a starter you might find yourself having a hard-time catching up with latest technologies because it will give you more confusion as to what sets of technologies to use and where to start. We know that there are tons of resources out there that you can use as a reference to learn but you still find it hard to connect the dots in the picture. Sometimes you might think of losing the interest to learn and give up. If you are confused and no idea how to start building a web app from scratch, then this series of article is for you.

Background

Building Web Application using Entity Framework and MVC 5 is targeted to beginners who want to jump on ASP.NET MVC 5 and get their hands dirty with practical example. I've written this article series in such a way that it’s easy to follow and understand by providing step-by-step process on creating a simple web application from scratch and deploying it to IIS Web Server. As you go along and until such time you finished following the series, you will learn how to create a database using SQL Server, learn the concept of ASP.NET MVC and what it is all about, learn Entity Framework using Database-First approach, learn basic jQuery and AJAX, learn to create pages such as Registration, Login, Profile and Admin page where user can modify, add and delete information. You will also learn how to install and deploy your application in IIS Web Server.

Prerequisites

Before you go any further make sure that you have basic knowledge on the following technologies:

  • SQL Server
  • Visual Studio
  • ASP.NET MVC and how stuff works in MVC
  • Entity Framework
  • C#
  • LINQ basic
  • Basics on HTML, CSS and JavaScript/jQuery ASP.NET MVC Overview

Environment and Development Tools

The following are the tools and environment settings that I am using upon building the web app.

  • Windows 8.1
  • IIS8
  • Visual Studio 2015
  • SQL Express 2014

Let's Get Started!

I’ll try to keep this demo as simple as possible so starters can easily follow. By “simple” I mean limit the talking about theories and concepts, but instead jumping directly into the mud and get your hands dirty with code examples.
Here's the list of the series:

What you will learn:

  • Creating a database in MS SQL Server
  • Brief overview of ASP.NET MVC
  • Implementing Entity Framework Database-First approach
  • Creating a simple Signup page

Before we start building an MVC application let’s talk about a bit about MVC because it is very important to know how the MVC framework works.

What is MVC?

ASP.NET MVC gives you a powerful, pattern-based way to build dynamic websites that enables a clean separation of concerns and that gives you full control over mark-up for enjoyable and agile development.

To make it more clear, here’s how I view the high-level process of MVC:

Unlike ASP.NET WebForms in which a request is going directly to a page file (.ASPX), in MVC when a user request a page, it will first talk to the Controller , process data when necessary and returns view Models to the View for the user to see.

Models

Model objects are the parts of the application that implement the logic for the application domain data. Often, model objects retrieved and store model state in database.

Controllers

Controllers are the components that handle user interaction, work with the model, and ultimately select a view to render in the browser.

Views

Views are the components that display the application’s user interface (UI), Typically this UI is created from the model data.

STEP 1: Creating a Database

Open SQL Server or SQL Server Express Management Studio and then create a database by doing the following:

  • Right click on the Databases folder
  • Select New Database
  • Enter a database name and then click OK. Note that for this example I used "DemoDB" as my database name.

The DemoDB database should be created like in the image below:

Note that you can also write a SQL script to create a database but since I'm a lazy one so I chose to use the GUI instead. :)

Now open a New Query window or just press CTRL + N to launch the query window and then run the following scripts:

LOOKUPRole table
USE [DemoDB]  
GO

CREATE TABLE [dbo].[LOOKUPRole](  
    [LOOKUPRoleID] [int] IDENTITY(1,1) NOT NULL,
    [RoleName] [varchar](100) DEFAULT '',
    [RoleDescription] [varchar](500) DEFAULT '',
    [RowCreatedSYSUserID] [int] NOT NULL,
    [RowCreatedDateTime] [datetime]  DEFAULT GETDATE(),
    [RowModifiedSYSUserID] [int] NOT NULL,
    [RowModifiedDateTime] [datetime] DEFAULT GETDATE(),
PRIMARY KEY (LOOKUPRoleID)  
    )
GO  

Adding sample data to LOOKUPRole table

INSERT INTO LOOKUPRole (RoleName,RoleDescription,RowCreatedSYSUserID,RowModifiedSYSUserID)  
       VALUES ('Admin','Can Edit, Update, Delete',1,1)
INSERT INTO LOOKUPRole (RoleName,RoleDescription,RowCreatedSYSUserID,RowModifiedSYSUserID)  
       VALUES ('Member','Read only',1,1)
SYSUser table
USE [DemoDB]  
GO

CREATE TABLE [dbo].[SYSUser](  
    [SYSUserID] [int] IDENTITY(1,1) NOT NULL,
    [LoginName] [varchar](50) NOT NULL,
    [PasswordEncryptedText] [varchar](200) NOT NULL,
    [RowCreatedSYSUserID] [int] NOT NULL,
    [RowCreatedDateTime] [datetime] DEFAULT GETDATE(),
    [RowModifiedSYSUserID] [int] NOT NULL,
    [RowMOdifiedDateTime] [datetime] DEFAULT GETDATE(),
    PRIMARY KEY (SYSUserID)
)

GO  
SYSUserProfile table
USE [DemoDB]  
GO


CREATE TABLE [dbo].[SYSUserProfile](  
    [SYSUserProfileID] [int] IDENTITY(1,1) NOT NULL,
    [SYSUserID] [int] NOT NULL,
    [FirstName] [varchar](50) NOT NULL,
    [LastName] [varchar](50) NOT NULL,
    [Gender] [char](1) NOT NULL,
    [RowCreatedSYSUserID] [int] NOT NULL,
    [RowCreatedDateTime] [datetime] DEFAULT GETDATE(),
    [RowModifiedSYSUserID] [int] NOT NULL,
    [RowModifiedDateTime] [datetime] DEFAULT GETDATE(),
    PRIMARY KEY (SYSUserProfileID)
    )
GO

ALTER TABLE [dbo].[SYSUserProfile]  WITH CHECK ADD FOREIGN KEY([SYSUserID])  
REFERENCES [dbo].[SYSUser] ([SYSUserID])  
GO  
And finally, the SYSUserRole table
USE [DemoDB]  
GO

CREATE TABLE [dbo].[SYSUserRole](  
    [SYSUserRoleID] [int] IDENTITY(1,1) NOT NULL,
    [SYSUserID] [int] NOT NULL,
    [LOOKUPRoleID] [int] NOT NULL,
    [IsActive] [bit] DEFAULT (1),
    [RowCreatedSYSUserID] [int] NOT NULL,
    [RowCreatedDateTime] [datetime] DEFAULT GETDATE(),
    [RowModifiedSYSUserID] [int] NOT NULL,
    [RowModifiedDateTime] [datetime] DEFAULT GETDATE(),
    PRIMARY KEY (SYSUserRoleID)
)
GO

ALTER TABLE [dbo].[SYSUserRole]  WITH CHECK ADD FOREIGN KEY([LOOKUPRoleID])  
REFERENCES [dbo].[LOOKUPRole] ([LOOKUPRoleID])  
GO

ALTER TABLE [dbo].[SYSUserRole]  WITH CHECK ADD FOREIGN KEY([SYSUserID])  
REFERENCES [dbo].[SYSUser] ([SYSUserID])  
GO  

That’s it. We just created 4 database tables. The next step is to create the web application.

STEP 2: Adding a new ASP.NET MVC 5 Project

Let’s go ahead and fire up Visual Studio 2015 and select File > New > Project. Under New Project dialog select Templates > Visual C# > ASP.NET Web Application. See image below for a clear view:

Name your project whatever you like and then click OK. In my case I named the project as “MVC5RealWorld”. Now after that you should be able to see the “New ASP.NET Project” dialog like in the following:

The New ASP.NET Project dialog for ASP.NET 4.6 templates allows you to select what type of project you want to create, configure any combination of technologies such as WebForms, MVC or Web API, configure unit test project, configure authentication option and also offers a new option to host your website in the Azure cloud. Adding to that it also provide templates for ASP.NET 5.

Note: In this post I will only be covering the creation of an MVC 5 application. So the details of each configuration like unit testing, authentication, hosting in cloud, etc. will not be covered.

Now select "Empty" under ASP.NET 4.6 templates and then check the MVC option under folders and core reference as shown from the image above. The reason for this is that we will create an empty MVC application from scratch. Click OK to let Visual Studio generate the necessary files and templates needed for you to run MVC.

You should be able to see something like below:

STEP 3: Setting up the Data Access

For this example, I’m going to use Database-First approach with Entity Framework 6 (EF) as our data access mechanism so that we can just program against the conceptual application model instead of programming directly against our database.

As a quick recap, a Model is just a class. Yes it’s a class that implements the logic for your application’s domain data. Often, model objects retrieved and store model state in database.

Now let’s setup our Model folder structure by adding the following sub-folders under the Models folder:

  • DB
  • EntityManager
  • ViewModel

Our model structure should now look something like below:

The "DB" folder is where we store our entity models (.EDMX file). To add an entity, right click on the DB folder and select Add > New Item > Data > ADO.NET Entity Data Model. See the image below for a clear view:

You can name your entity model as you would like but for this example I just named it as "DemoModel" for simplicity. Now click Add to continue and on the next step select "EF Designer from Database" as we are going to use database first approach to work with existing database. Click Next to proceed. In the next step click on "New Connection" button and then select "Microsoft SQL Server (SqlClient)" as the data source then click Next. You should see this dialog below:

Supply the SQL server name and select the database that we have just created in STEP 1. If you have an existing database, then use that instead. Also note that I am using windows authentication for logging in to my SQL Server. Once you’ve done supplying the necessary fields then click on "Test Connection" to verify the connectivity. If it is successful then just click OK.

You should now see the following dialog below:

Noticed that the connection string was automatically generated for you. Click next and then select Entity Framework 6.x to bring the following dialog:

Now select the table(s) that you would want to use in your application. For this example I selected all tables because we are going to use those in our application. Clicking the Finish button will generate the entity model for you as shown in the image below:

What happened there is that EF automatically generates the business objects for you and let you query against it. The EDMX or the entity data model will serve as the main gateway by which you retrieve objects from database and resubmit changes.

STEP 4: Creating the Signup Page

Adding ViewModels

Just to recap Entity Framework will generate the business model objects and manage data access within the application. As a result, the classes LOOKUPRole, SYSUserRole, SYSUser and SYSUserProfile are automatically created by EF and it features all the fields from the database table as properties of the class.

I don't want to use these classes directly in the View so I decided to create a separate class that just holds the properties I needed in the View. Now let's add the “UserModel” class by right-clicking on the "ViewModel" folder then select Add > Class. The "UserModel.cs" file is where we put all user related model views. For the Signup page we are going to add the "UserSignUpView" class that houses the following properties:

using System.ComponentModel.DataAnnotations;

namespace MVC5RealWorld.Models.ViewModel  
{
    public class UserSignUpView
    {
        [Key]
        public int SYSUserID { get; set; }
        public int LOOKUPRoleID { get; set; }
        public string RoleName { get; set; }
        [Required(ErrorMessage = "*")]
        [Display(Name = "Login ID")]
        public string LoginName { get; set; }
        [Required(ErrorMessage = "*")]
        [Display(Name = "Password")]
        public string Password { get; set; }
        [Required(ErrorMessage = "*")]
        [Display(Name = "First Name")]
        public string FirstName { get; set; }
        [Required(ErrorMessage = "*")]
        [Display(Name = "Last Name")]
        public string LastName { get; set; }
        public string Gender { get; set; }
    }
}

Noticed that I added the Required and DisplayName attributes for each property in the UserSignUpView class. These attributes are called Data Annotations. Data annotations are attribute classes that lives under System.ComponentModel.DataAnnotations namespace that you can use to decorate classes or properties to enforce pre-defined validation rules.

I'll use this validation technique because I want to keep a clear separation of concerns by using the MVC pattern and couple that with data annotations in the model, then your validation code becomes much simpler to write, maintain, and test.

Adding the UserManager Class

The next step that we will do is to create the "UserManger" class that would handle the CRUD operations (Create, Read, Update and Delete operations) of a certain table. The purpose of this class is to separate the actual data operations from our controller and to have a central class for handling insert, update, fetch and delete operations.

Notes:

  • Please keep in mind that in this step I'm only be doing the insert part in which a user can add new data from the View to the database. I'll talk about how to do update, fetch and delete with MVC in my next article. So this time we'll just focus on the insertion part first.
  • Since this demo is intended to make web app as simple as possible then I will not be using TransactionScope and Repository pattern. In real complex web app you may want to consider using TransactionScope and Repository for your Data Access.

Now right click on the "EntityManager" folder and then add a new class by selecting Add > Class and name the class as "UserManager". Here's the code block for the "UserManager.cs" class:

using System;  
using System.Linq;  
using MVC5RealWorld.Models.DB;  
using MVC5RealWorld.Models.ViewModel;

namespace MVC5RealWorld.Models.EntityManager  
{
    public class UserManager
    {
        public void AddUserAccount(UserSignUpView user) {

            using (DemoDBEntities db = new DemoDBEntities()) {

                SYSUser SU = new SYSUser();
                SU.LoginName = user.LoginName;
                SU.PasswordEncryptedText = user.Password;
                SU.RowCreatedSYSUserID = user.SYSUserID > 0 ? user.SYSUserID : 1;
                SU.RowModifiedSYSUserID = user.SYSUserID > 0 ? user.SYSUserID : 1; ;
                SU.RowCreatedDateTime = DateTime.Now;
                SU.RowMOdifiedDateTime = DateTime.Now;

                db.SYSUsers.Add(SU);
                db.SaveChanges();

                SYSUserProfile SUP = new SYSUserProfile();
                SUP.SYSUserID = SU.SYSUserID;
                SUP.FirstName = user.FirstName;
                SUP.LastName = user.LastName;
                SUP.Gender = user.Gender;
                SUP.RowCreatedSYSUserID = user.SYSUserID > 0 ? user.SYSUserID : 1;
                SUP.RowModifiedSYSUserID = user.SYSUserID > 0 ? user.SYSUserID : 1;
                SUP.RowCreatedDateTime = DateTime.Now;
                SUP.RowModifiedDateTime = DateTime.Now;

                db.SYSUserProfiles.Add(SUP);
                db.SaveChanges();


                if (user.LOOKUPRoleID > 0) {
                    SYSUserRole SUR = new SYSUserRole();
                    SUR.LOOKUPRoleID = user.LOOKUPRoleID;
                    SUR.SYSUserID = user.SYSUserID;
                    SUR.IsActive = true;
                    SUR.RowCreatedSYSUserID = user.SYSUserID > 0 ? user.SYSUserID : 1;
                    SUR.RowModifiedSYSUserID = user.SYSUserID > 0 ? user.SYSUserID : 1;
                    SUR.RowCreatedDateTime = DateTime.Now;
                    SUR.RowModifiedDateTime = DateTime.Now;

                    db.SYSUserRoles.Add(SUR);
                    db.SaveChanges();
                }
            }
        }

        public bool IsLoginNameExist(string loginName) {
            using (DemoDBEntities db = new DemoDBEntities()) {
                return db.SYSUsers.Where(o => o.LoginName.Equals(loginName)).Any();
            }
        }
    }
}

The AddUserAccount() is a method that inserts data to the database using Entity Framework. The IsLoginNameExist() is a method that returns boolean. It checks the database for an existing data using LINQ syntax.

Adding the Controllers

Since our model was already set then let's go ahead and add the "AccountController". To do this, just right click on the "Controllers" folder and select Add > Controller > MVC 5 Controller - Empty and then click Add. In the next dialog name the controller as "AccountController" and then click Add to generate class for you.

Here's the code block for the AccountController:

using System.Web.Mvc;  
using System.Web.Security;  
using MVC5RealWorld.Models.ViewModel;  
using MVC5RealWorld.Models.EntityManager;

namespace MVC5RealWorld.Controllers  
{
    public class AccountController : Controller
    {      
        public ActionResult SignUp() {
            return View();
        }

        [HttpPost]
        public ActionResult SignUp(UserSignUpView USV) {
            if (ModelState.IsValid) {
                UserManager UM = new UserManager();
                if (!UM.IsLoginNameExist(USV.LoginName)) {
                    UM.AddUserAccount(USV);
                    FormsAuthentication.SetAuthCookie(USV.FirstName, false);
                    return RedirectToAction("Welcome", "Home");

                }
                else
                    ModelState.AddModelError("", "Login Name already taken.");
            }
            return View();
        }
    }
}

The AccountController has two main methods. The first one is the "SignUp" which returns the "SignUp.cshtml" View. The second one also named as "SignUp" but it is decorated with the "[HttpPost]" attribute. This attribute specifies that the overload of the "SignUp" method can be invoked only for POST request.

The second method is responsible for inserting a new entry into the database and automatically authenticate the users using FormsAuthentication.SetAuthCookie() method. This method creates an authentication ticket for the supplied user name and adds it to the cookies collection of the response or to the URL if you are using cookieless authentication. After authenticating, we then redirect the users to the Welcome.cshtml page.

Now add another Controller and name it as "HomeController". This controller would be our controller for our default page. We will create the "Index" and the "Welcome" Views for this controller in the next step. Here's the code for the "HomeController" class:

using System.Web.Mvc;

namespace MVC5RealWorld.Controllers  
{
    public class HomeController : Controller
    {
        public ActionResult Index() {
            return View();
        }

        [Authorize]
        public ActionResult Welcome() {
            return View();
        }
    }
}

The HomeController class consists of two ActionResult methods such as Index and Welcome. The "Index" method serve as our default redirect page and the "Welcome" method will be the page where we redirect users after they have successfully registered. We also decorated it with the "[Authorize]" attribute so that this method will only be available for the logged-in users. Keep in mind that since we didn't specify the view name in the View() method above, the framework will automatically map the action name to the name of the view. In this case the Index action will map to the Index.cshtml file and the Welcome action will map to the Welcome.cshtml file.

To configure a default page you can go to App_Start > RouteConfig. From there you should be able to see something like this:

public static void RegisterRoutes(RouteCollection routes)  
{
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
}
Adding the Views

There are two possible ways to add Views. You can either manually create the Views folder by yourself and add the corresponding .CSHTML files or by right clicking on the Controller’s action method just like in the following:

Clicking "Add View" will show this dialog below:

Just click Add since we don’t need to do anything with the Index page. Now modify the Index page and replace it with the following HTML markup:

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Index</h2>  
<br/>  
No Account yet? @Html.ActionLink("Signup Now!", "SignUp", "Account")

The ActionLink in the markup above allows you to navigate to the SignUp page which lives under the Account controller. Now add a View to the Welcome action by doing the same as what we did by adding the Index page. Here’s the Welcome page HTML markup:

@{
    ViewBag.Title = "Welcome";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Hi <b>@Context.User.Identity.Name</b>! Welcome to my first MVC 5 Web App!</h2>

Now switch back to AccountController and add a new View for the SignUp page. In the Add View dialog select “Create” as the scaffold template, select the UserSignUpView as the model and select DemoDBEntities as the data context as shown in the image below:

Click Add to let Visual Studio scaffolds the UI for you. The term "Scaffolding" allows you to quickly generate the UI that you can edit and customize.

Now we need to trim down the generated fields because there are some fields that we don’t actually need users to see like the RoleName and ID’s. Adding to that I also modified the Password to use the PasswordFor HTML helper and use DropDownListFor for displaying the Gender. Here’s the trimmed down HTML markup for the SignUp page:

@model MVC5RealWorld.Models.ViewModel.UserSignUpView

@{
    ViewBag.Title = "SignUp";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>SignUp</h2>

@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.LoginName, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.LoginName, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.LoginName, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Password, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.PasswordFor(model => model.Password, new  { @class = "form-control" } )
                @Html.ValidationMessageFor(model => model.Password, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.FirstName, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.LastName, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Gender, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownListFor(model => model.Gender, new List<SelectListItem> {
                    new SelectListItem { Text="Male", Value="M" },
                    new SelectListItem { Text="Female", Value="F" }
                }, new { @class = "form-control" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Register" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>  
    @Html.ActionLink("Back to Main", "Index","Home")
</div>

The markup above is a strongly-type view. This strongly typed approach enables better compile-time checking of your code and richer IntelliSense in the Visual Studio editor. By including the @model statement at the top of the view template file, you can specify the type of model that the view expects. In this case it uses the MVC5RealWorld.Models.ViewModel.UserSignUpView.

If you also noticed, after adding the views, Visual Studio automatically structures the folders for your Views:

STEP 5: Web.Config

The final piece is to add the following configuration under system.web node to enable forms authentication:

<system.web>  
    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login" defaultUrl="~/Home/Welcome">                      
      </forms>
    </authentication>    
</system.web>  

STEP 6: Running the Application

The following are outputs from running the page in the browser:

On initial load

Upon page validation

Supplying the required fields

After successful registration

That’s it! I hope you will find this article useful.

Check-out the next article here: Building Web Application using Entity Framework and MVC 5: Part 2

Source Code

Source code for this series can be found here. You can also download it at CodeProject here.