This article explains the following:
- Creating a database in Microsoft SQL Server
- Brief overview of ASP.NET MVC
- Implementing Entity Framework Database-First approach
- Creating a simple Signup page
Before you go any further ensure that you have basic knowledge of the following technologies:
- SQL Server
- Visual Studio
- ASP.NET MVC and how stuff works in MVC
- Entity Framework
- C#
- Basics of HTML, CSS and JavaScript/jQuery
This article will guide you through the basic procedure of how to create a simple web application using ASP.NET MVC 5 with a real-world example using Entity Framework Database-First approach. I'll try to keep this demo as simple as possible so beginners can easily follow. You can skip STEP 1 if you already have an existing database. Here's the list of the series for this application:
ASP.NET MVC Overview
Before we start building an MVC application let's talk a bit about MVC because it is very important to understand how the MVC framework works.
MVC
ASP.NET MVC gives you a powerful, pattern-based way to build dynamic websites that enable 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 requests a page, it will first talk to the Controller, process data when necessary and return ViewModels 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 retrieve and store model state in a 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 in this demo 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.
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
- 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
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. In the New Project dialog select Templates > Visual C# > ASP.NET Web Application. See the image below for a clear view:
Name your project whatever you like and then click OK. Note that for this demo I named the project “MVC5RealWorld”. Now after that you should be able to see the “New ASP.NET Project” dialog. See the image below:
The New ASP.NET Project dialog for ASP.NET 4.6 templates allow 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.
In this article 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 and so on 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 as in the following:
STEP 3: Setting up the Data Access
For this example, I'm going to use Database-First 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:
Our model structure should look something as in the following:
The DB folder is where we store our entity model (EDMX). 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 “DemoModel” for simplicity. Now click Add to continue and on the next step select “EF Designer from Database” since we will use the database first approach to work with existing database. Click Next to proceed. In the next step click on the “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 into my SQL Server. Once you've done supplying the necessary field then click on “Test Connection” to verify the connectivity. If it is successful then just click OK.
You should now see the following dialog:
Notice that the connection string was automatically generated for you. Click Next and then select Entity Framework 6.x to bring up this dialog below:
Now select the table(s) that you want to use in your application. For this example I selected all the tables because we will 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 lets you query against it. The EDMX or the entity data model will serve as the main gateway by which you retrieve objects from the 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 will add the “UserSignUpView” class. So in the UserModel file add the following class:
- 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; }
- }
- }
Notice 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 live under the 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 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.
For more information about Data Annotations refer to Data Annotations. And of course you can find more examples about it by doing a simple search at Google or Bing.
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 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 a web app as simple as possible I will not be using TransactionScope and Repository pattern. In a 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 "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, 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 "AccountController" and then click Add to generate the class.
Now here's the code block for "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" that returns the "SignUp.cshtml" View. The second one is also named "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 requests.
The second method is responsible for inserting a new entry into the database and automatically authenticate the users using the 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 "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 serves as our default redirect page and the "Welcome" method will be the page where we redirect the users after they have successfully registered. We have also decorated it with the "[Authorize]" attribute so that this method will only be available for the logged-in users.
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 yourself and add the corresponding .CSHTML files or by right-clicking on the Controller's action method just like in the image shown below:
Clicking Add View will show this dialog below:
Just click Add since we don't need to do anything with the Index page for this demo. 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 that lives under the Account controller. Now add a View to the Welcome action by doing the same as 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 View for the SignUp page. In the Add View dialog select “Create” as the scaffold template, select the UserSignUpView as the model and the 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 IDs. 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 a @model statement at the top of the view template file, you can specify the type of object 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. See the image below:
STEP 5: Running the application
The following are the outputs from running the page in the browser.
On initial load
Page validation triggers
Supplying the required fields
After successful registration
That's it! I hope you will find this article useful. J
In my next article I'll cover how to create a Login page so stay tuned.