Thursday, September 5, 2013

MVC TUTORIAL VOLUME I


Installing asp.net mvc - Part 1

Suggested Video Tutorials
1. Dot Net Basics
2. C# Tutorial
3. SQL Tutorial
4. ADO.NET Tutorial
5. ASP.NET Tutorial
6. GridView Tutorial



In this video we will discuss about installing asp.net mvc. Before installing asp.net mvc, first let's determine what version is already installed. Go to "Control Panel" and click on "Programs and Features". On my computer at the moment I only have asp.net mvc 2 as shown below.


Since I just have mvc 2. I only can create asp.net mvc 2 web applications using visual studio. If you want to create asp.net mvc 3 or 4 applications, then please download and install asp.net mvc 3 and 4 from the following locations.

ASP.NET MVC 3
http://www.asp.net/mvc/mvc3

ASP.NET MVC 4
http://www.asp.net/mvc/mvc4

After successful installation of mvc 3 and 4, Programs and Features window on my computer is as shown below.
https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwWtwDtoEk5dO3FGf7l9YS9TmLxDtaw2LZ8JU3TpJC06ZKgGSi_c_hvCG9NwLXbo6UMXCpf4W566El6sjClsOZsKrV2gu0YfNRha9eVx1Iq6dxvrAno9sk8lWw3CPHOC3u8nSw6JLunsMA/s1600/installed+asp.net+mvc+4+versions.png

When you create a new project, make sure you have .NET Framework 4 selected in the drop down list. If you have any other earlier .net framework versions selected, then ASP.NET MVC 3 Web Application and ASP.NET MVC 4 Web Application templates will not be available.

https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGTppPl48JCpi42klomdDqtOWi0NNPtcTE6PF0Q0cZOJTvLKxE0A7HD0XF0rB5AMXrvYzQNjEr_cAxv-q978IM9yjZn0P1dEf979OU_jY473bo-W9EF5jYZvlU5r4vp1iAPYTLNZS1NMjs/s1600/installed+asp.net+mvc+versions.png
 

 


What asp.net mvc version is my mvc application using - Part 2

Suggested Video
Part 1 - Installing MVC

In this video we will discuss about
How to determine what version of asp.net mvc is being used by an existing mvc application



There are 2 ways to do this
1. At design time - In the solution explorer, expand "References" folder. Right click on System.Web.Mvc assembly and select "Properties" and you can find the version that's used as shown below.
https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyhMLtaDJaYVQhr596DwPbheKsjtx9gsc78ojvlMRAfj27nO5pRAyB4A4nO7XRNwVrp0PDv8xcMO16ZPRJVysKV4m0_PvpqWyt_iHHV1Wda997Dir1PiuJh810qUP-PiUTtF4eQc6Jh7sI/s1600/asp.net+mvc+version.png

2. At runtime using code -
typeof(Controller).Assembly.GetName().Version.ToString()

Creating your first asp.net mvc application - Part 3

Suggested Videos 
Part 1 - Installing asp.net mvc
Part 2 - Which asp.net mvc version is my mvc application using



In this video we will discuss about
1. Creating an asp.net mvc application
2. Understand how mvc request is processed as apposed to webform request



Creating an mvc application:
1. Open visual studio
2. Click File > New Project
3. Select "Web" from "Installed Templates" section
4. Select ASP.NET MVC 4 Web Application
5. Set Name="MVCDemo"
6. Click OK
7. Select "Empty" template. Select "Razor" as the ViewEngine. There are 2 built in view engines - Razor and ASPX. Razor is preferred by most mvc developers. We will discuss about Razor view engine in detail in a later video session.
8. At this point you should have an mvc application created.

Notice that in the solution explorer, you have several folders - Models, Views, Controllers etc. As the names suggest these folders are going to contain Models, Views, and Controllers. We will discuss about Models, Views, and Controllers in a later video session.

Now let's add a controller to our project
1. Right Click on "Controllers" folder
2. Select Add > Controller
3. Set Controller Name = HomeController
4. Leave rest of the defaults and click "Add"

We should have HomeController.cs added to "Controllers" folder.

At this point run the application by pressing CTRL+F5. Notice that you get an error as shown below.
https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_I94t_WF4SzFxRm0Zdg9beBFrJJfad_P55B5u6TqlXUTvYb_t4m1diY11D-_iLSHx0hBX35sKeZn3tgaJNGJvxM6JMCSee4_Dlu_ZzTGrQC3LxMLUqD3ywvDlrjz91kTCwv6xBHfSNlU8/s1600/asp.net+mvc+error.png

To fix this error, we need to add a view with name, "Index". We will discuss about views in detail in a later video session. Let's fix it another way. The following is the function that is automatically added to HomeController class
public ActionResult Index()
{
   
return View();
}

Change the return type of Index() function from
"ActionResult" to "string", and return string "Hello from MVC Application" instead of View().
public string Index()
{
   
return "Hello from MVC Application";
}

Run the application and notice that, the string is rendered on the screen. When you run the application, by default it is using built-in asp.net development server. Let's use IIS, to run the application instead of the built-in asp.net development server.
1. In the solution explorer, right click on the project and select "Properties"
2. Click on "Web" tab
3. Select "Use Local IIS Web Server" radio button
4. Notice that the Project Url is set to http://localhost/MVCDemo by default
5. Finally click on "Create Virtual Directory" button
https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQWFvNpVe5dBdtwg_OyE3I4NMWCYWb196LpFGFjp1-by3oftc2DhbQGFx0Ufu8dE3QuKt7LpwRdw01VW7AJy24J5yLEN0G99z1gwV0VMoenAtDMCRO_EGqBOh_5YsmAKRIWpsx-e7GZ-Xx/s1600/configuring+iis+to+run+mvc+application.png

Run the application, and notice that the URL is "http://localhost/MVCDemo/"

Now change the URL to "http://localhost/MVCDemo/Home/index"

In the URL "Home" is the name of the controller and "Index" is the method within HomeController class.

So the improtant point to understand is that the URL is mapped to a controller action method. Where as in web forms application, the URL is mapped to a physical file. For example, in a web application, if we have to display the same message.
1. We add a webform and in the Page_load() event include Response.Write("Hello from ASP.NET Web Forms Application");
2. We then access WebForm1.aspx as shown below
http://localhost/WebFormsApplication/WebForm1.aspx
3. The Page load event gets executed and the message string is displayed.

Controllers in an mvc application - Part 4

Suggested Videos 
Part 1 - Installing asp.net mvc
Part 2 - Which asp.net mvc version is my mvc application using
Part 3 - Creating your first asp.net mvc application



In this video we will discuss about controllers. Please watch Part 3 of MVC tutorial before proceeding. In Part 3, we discussed that, the URL - http://localhost/MVCDemo/Home/Index will invoke Index() function of HomeController class. So, the question is, where is this mapping defined. The mapping is defined in Global.asax. Notice that in Global.asax we have RegisterRoutes() method.
RouteConfig.RegisterRoutes(RouteTable.Routes);



Right click on this method, and select "Go to Definition". Notice the implementation of RegisterRoutes() method in RouteConfig class. This method has got a default route.
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 }
    );
}

The following URL does not have id. This is not a problem because id is optional in the default route.
http://localhost/MVCDemo/Home/Index

Now pass id in the URL as shown below and press enter. Nothing happens.
http://localhost/MVCDemo/Home/Index/10

Change the Index() function in HomeController as shown below.
public string Index(string id)
{
    return
"The value of Id = " + id;
}

Enter the following URL and press enter. We get the output as expected.
http://localhost/MVCDemo/Home/Index/10

In the following URL, 10 is the value for id parameter and we also have a query string "name".
http://localhost/MVCDemo/home/index/10?name=Pragim

Change the Index() function in HomeController as shown below, to read both the parameter values.
public string Index(string id, string name)
{
   
return "The value of Id = " + id + " and Name = " + name;
}

Just like web forms, you can also use "Request.QueryString"
public string Index(string id)
{
   
return "The value of Id = " + id + " and Name = " + Request.QueryString["name"];
}

Part 5 - Views in an mvc application

Suggested Videos 
Part 2 - Which asp.net mvc version is my mvc application using
Part 3 - Creating your first asp.net mvc application
Part 4 - Controllers in an mvc application



In this video we will discuss views. Please watch Part 4 of MVC tutorial before proceeding. Let's change the Index() method in HomeController to return a list of country names. Since country names are strings, return List<string>. Get rid of id and name parameters.
public List<string> Index()
{
   
return new List<string>()
    {
       
"India",
       
"US",
       
"UK",
       
"Canada"
    };
}



Run the application and notice that the output is not as expected. 
System.Collections.Generic.List`1[System.String]

To correct this problem, let's add a view to our project.
1. Right click on the Index() function
2. Click on "Add View"
3. Notice that, the view name in "Add View" dialog box matches the name of the controller action method
4. Select "Razor" as the view engine
5. Leave the rest of the defaults as is, and click "Add" button

Make the following modifications to the Index() function of the HomeController class, so that, the HomeController returns a view instead of List<string>.
// Change the return type from List<string> to ActionResult
public ActionResult Index()
{
    // Store the list of Countries in ViewBag.  
    ViewBag.Countries =
new List<string>()
    {
        
"India",
        
"US",
        
"UK",
        
"Canada"
    };

   
// Finally return a view
   
return View();
}

We will discuss ViewBag & ViewData, and the differences between them in our next video session. For now, understand that, ViewBag & ViewData is a mechanism to pass data from the controller to the view.

Please Note: To pass data from controller to a view, It's always a good practice to use strongly typed view models instead of using ViewBag & ViewData. We will discuss view models in a later video session.

Now, copy and paste the following code in "Index.cshtml" view
@{
    ViewBag.Title =
"Countries List";
}

<h2>Countries List</h2>

<ul>

@
foreach (string strCountry in ViewBag.Countries)
{
   
<li>@strCountry</li>
}

</ul>

Please Note: We use "@" symbol to switch between html and c# code. We will discuss razor views and their syntax in a later video session.

Part 6 - ViewData and ViewBag in mvc

Suggested Videos 
Part 3 - Creating your first asp.net mvc application
Part 4 - Controllers in an mvc application
Part 5 - Views in an mvc application



In this video we will discuss
1. What is ViewData
2. What is ViewBag
3. Difference between ViewData and ViewBag



Both ViewData and ViewBag are used to pass data from a controller to a view. ViewData is a dictionary of objects that are stored and retrieved using strings as keys. The syntax of ViewData is very similar to that of ViewState, SessionState and ApplicationState.
// Storing data in ViewData
ViewData[
"YourData"] = "SomeData";

// Retrieving data from ViewData
string strData = ViewData["YourData"].ToString();

ViewData does not provide compile time error checking. For example, if you mis-spell the key names you wouldn't get any compile time error. You get to know about the error only at runtime.

ViewBag uses the dynamic feature that was introduced in to C# 4.0. It allows an object to have properties dynamically added to it. Using ViewBag the above code can be rewritten as below.
// Storing data in ViewBag
ViewBag.YourData =
"SomeData";

// Retrieving data from ViewBag
string strData = ViewBag.YourData;

Just like ViewData, ViewBag does not provide compile time error checking. For example, if you mis-spell the property name, you wouldn't get any compile time error. You get to know about the error only at runtime.

Internally ViewBag properties are stored as name/value pairs in the ViewData dictionary.

Please Note: To pass data from controller to a view, It's always a good practice to use strongly typed view models instead of using ViewBag & ViewData. Strongly typed view models  provide compile time error checking. We will discuss view models in a later video session.

Part 7 - Models in an mvc application

Suggested Videos 
Part 4 - Controllers in an mvc application
Part 5 - Views in an mvc application
Part 6 - ViewData and ViewBag in mvc



In this video we will discuss models in an mvc application.

Let's understand models with an example. We want to retrieve an employee information from tblEmployee table and display it as shown below.
https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmQa9_2FkOlJ70tU6M_IEKpPMv75Ffqkz-mKK8265KSpi_8o0wKbEdG_MKLJSA7gSc5inY3UzaRrhLyHF5fssqL9qr1W8GQg7L-W9bkOYO47qX0ojSPDgPDxyyUfYK2HfGxse6IJ-XpTbG/s1600/Models+in+mvc.png



To encapsulate Employee information, add Employee model class to the Models folder. To do this
1. Right click on "Models" folder > Add > Class
2. Name the class as Employee.cs
3. Click "Add"

Copy and paste the following code in Employee.cs class file.
public class Employee
{
   
public int EmployeeId { get; set; }
   
public string Name { get; set; }
    
public string Gender { get; set; }
    
public string City { get; set; }
}

Now let's Add EmployeeController class to "Controllers" folder. To do this
1. Right click on "Controllers" folder > Add > Controller
2. Use EmployeeController as the name
3. Click "Add"

We want to use "Employee" model class in EmployeeController. So copy and paste the following "using" statement in "EmployeeController.cs"
using MVCDemo.Models;

By default an Index() Action method is created in EmployeeController. Change the name of the function to Details(). Create an instance of Employee class. For now we will hard code Employee data in this class. In a later video session, we will discuss about retrieving employee information from the database table tblEmployee. At this point EmployeeController should look as shown below.
public ActionResult Details()
{
   
Employee employee = new Employee()
    {
        EmployeeId = 101,
        Name =
"John",
        Gender =
"Male",
        City =
"London"
    };
         
   
return View();
}

Now, we need to pass the employee model object that we constructed in EmployeeController to a view, so the view can generate the HTML and send it to the requested client. To do this we first need to add a view. To add a view
1. Right click on Details() action method and select "Add View" from the context menu
2. Set
    a)View Name = Details
    b)View Engine = Razor
    c)Select "Create strongly typed view" check box
    d)From the "Model class" dropdownlist, select "Employee (MVCDemo.Models)"
    Note: If Employee class is not visible in the dropdownlist, please build your project and then try adding the view again.
3. Finally click "Add"

At this point, Details.cshtml should be added to "Employee" folder. Please note that "Employee" folder is automatically created and added to "Views" folder.

Copy and paste the following code in Details.cshtml file.
@model MVCDemo.Models.Employee

@{
    ViewBag.Title =
"Employee Details";
}

<h2>Employee Details</h2>

<table style="font-family:Arial">
    
<tr>
        
<td>
            Employee ID:
        
</td>
        
<td>
            @Model.EmployeeId
        
</td>
    
</tr>
    
<tr>
        
<td>
            Name:
        
</td>
        
<td>
            @Model.Name
        
</td>
    
</tr>
    
<tr>
        
<td>
            Gender:
        
</td>
        
<td>
            @Model.Gender
        
</td>
    
</tr>
    
<tr>
        
<td>
            City:
        
</td>
        
<td>
            @Model.City
        
</td>
   
</tr>
</table>

At this point if you run the project, and if you navigate to the following URL, you get a runtime error stating -
Object reference not set to an instance of an object.
http://localhost/MVCDemo/Employee/Details

To fix this error, pass "Employee" object to the view. The "return" statement in Details() action method need to be modified as shown below.
return View(employee);

That's it. Run the application and navigate to http://localhost/MVCDemo/Employee/Details. We should get the output as expected.

Part 8 - Data access in mvc using entity framework

Suggested Videos 
Part 5 - Views in an mvc application
Part 6 - ViewData and ViewBag in mvc
Part 7 - Models in MVC



The controller responds to URL request, gets data from a model and hands it over to the view. The view then renders the data. Model can be entities or business objects.

In part 7, we have built Employee entity.
public class Employee
{
   
public int EmployeeId { get; set; }
   
public string Name { get; set; }
    
public string Gender { get; set; }
    
public string City { get; set; }
}



In this video, we will discuss, retrieving data from a database table tblEmployee using entity framework. In a later video, we will discuss using business objects as our model.

Step 1: Install entity framework, if you don't have it installed already on your computer. At the time of this recording the latest version is 5.0.0.0. Using nuget package manager, is the easiest way to install. A reference to EntityFramework.dll is automatically added.
Open visual studio > Tools > Library Package Manager > Manage NuGet Packages for Solution

Step 2: Add EmployeeContext.cs class file to the Models folder. Add the following "using" declaration.
using System.Data.Entity;

Copy & paste the following code in EmployeeContext.cs
public class EmployeeContext : DbContext
{
   
public DbSet<Employee> Employees {get; set;}
}

EmployeeContext class derives from DbContext class, and is responsible for establishing a connection to the database. So the next step, is to include connection string in web.config file.

Step 3: Add a connection string, to the web.config file, in the root directory.
<connectionStrings>
  
<add name="EmployeeContext"
       
connectionString="server=.; database=Sample; integrated security=SSPI"
       
providerName="System.Data.SqlClient"/>
</connectionStrings>

Step 4: Map "Employee" model class to the database table, tblEmployee using "Table" attribute as shown below.
[
Table("tblEmployee")]
public class Employee
{
   
public int EmployeeId { get; set; }
   
public string Name { get; set; }
    
public string Gender { get; set; }
    
public string City { get; set; }
}

Note: "Table" attribute is present in "System.ComponentModel.DataAnnotations.Schema" namespace.

Step 5: Make the changes to "Details()" action method in "EmployeeController" as shown below.
public ActionResult Details(int id)
{
   
EmployeeContext employeeContext = new EmployeeContext();
   
Employee employee = employeeContext.Employees.Single(x => x.EmployeeId == id);

   
return View(employee);
}

Step 6: Finally, copy and paste the following code in Application_Start() function, in Global.asax file. Database class is present "in System.Data.Entity" namespace. Existing databases do not need, database initializer so it can be turned off.
Database.SetInitializer<MVCDemo.Models.EmployeeContext>(null);

That's it, run the application and navigate to the following URL's and notice that the relevant employee details are displayed as expected
http://localhost/MVCDemo/Employee/details/1
http://localhost/MVCDemo/Employee/details/2

Part 9 - Generate hyperlinks using actionlink html helper

Suggested Videos 
Part 6 - ViewData and ViewBag in mvc
Part 7 - Models in MVC
Part 8 - Data access in MVC using entity framework



In this video we will discuss, generating hyperlinks using actionlink html helper, for navigation between MVC pages.

Please watch Part 8, before proceeding.



We want to display all the employees in a bulletted list as shown below. Notice that all the employee names are rendered as hyperlinks.
Adding Links between pages using html helpers

When the hyperlink is clicked, the user will be redirected to employee details page, displaying the full details of the employee as shown below.
Generate links using Html.ActionLink html helper

Copy and paste the following Index() action method in EmployeeController class. This method retrieves the list of employees, which is then passed on to the view for rendering.
public ActionResult Index()
{
   
EmployeeContext employeeContext = new EmployeeContext();
   
List<Employee> employees = employeeContext.Employees.ToList();

   
return View(employees);
}

At the moment, we don't have a view that can display the list of employees. To add the view
1. Right click on the Index() action method
2. Set
View name = Index
View engine = Razor
Select, Create a stronlgy-typed view checkbox
Select "Employee" from "Model class" dropdownlist
3. Click Add

At this point, "Index.cshtml" view should be generated. Copy and paste the following code in "Index.cshtml".
@model
IEnumerable<MVCDemo.Models.Employee>

@
using MVCDemo.Models;

<div style="font-family:Arial">
@{
    ViewBag.Title =
"Employee List";
}

<h2>Employee List</h2>
<ul>
@
foreach (Employee employee in @Model)
{
   
<li>@Html.ActionLink(employee.Name, "Details", new { id = employee.EmployeeId })</li>
}
</ul>
</div>

Points to Remember:
1. @model is set to
IEnumerable<MVCDemo.Models.Employee>
2. We are using Html.ActionLink html helper to generate links

Copy and paste the following code in Details.cshtml
@Html.ActionLink(
"Back to List", "Index")

Part 10 - Working with multiple tables in mvc

Suggested Videos 
Part 7 - Models in MVC
Part 8 - Data access in MVC using entity framework
Part 9 - Generate hyperlinks using actionlink html helper



Please watch Part 9, before proceeding.

In this video we will discuss working with 2 related tables in MVC
1. tblDepartment
2. tblEmployee



Use the sql script to create and populate these 2 tables
Create table tblDepartment
(
 Id
int primary key identity,
 Name
nvarchar(50)
)

Insert into tblDepartment values('IT')
Insert into tblDepartment values('HR')
Insert into tblDepartment values('Payroll')

Create table tblEmployee
(
 EmployeeId
int Primary Key Identity(1,1),
 Name
nvarchar(50),
 Gender 
nvarchar(10),
 City 
nvarchar(50),
 DepartmentId
int
)

Alter table tblEmployee
add foreign key (DepartmentId)
references tblDepartment(Id)

Insert into tblEmployee values('Mark','Male','London',1)
Insert into tblEmployee values('John','Male','Chennai',3)
Insert into tblEmployee values('Mary','Female','New York',3)
Insert into tblEmployee values('Mike','Male','Sydeny',2)
Insert into tblEmployee values('Scott','Male','London',1)
Insert into tblEmployee values('Pam','Female','Falls Church',2)
Insert into tblEmployee values('Todd','Male','Sydney',1)
Insert into tblEmployee values('Ben','Male','New Delhi',2)
Insert into tblEmployee values('Sara','Female','London',1)

This is what we want to achieve
1. Display all the departments from tblDepartments table. The Department names should be rendered as hyperlinks.
2. On clicking the department name link, all the employees in the department should be displayed. The employee names should be rendered as hyperlinks.
3. On clicking the employee name link, the full details of the employee should be displayed.
4. A link should also be provided on the employee full details page to navigate back to Employee list page. Along the same lines, a link should also be provided on the employee list page to navigate back to Departments list page.

The screen shots of the above workflow is shown below for your reference
Working with multiple tables

Implementing Departments List:
Step 1: Right click on the "Models" folder and add a class file with name=Department.cs. Copy and paste the following code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations.Schema;

namespace MVCDemo.Models
{
    [
Table("tblDepartment")]
   
public class Department
    {
       
public int ID { get; set; }
       
public string Name { get; set; }
       
public List<Employee> Employees { get; set; }
    }
}

Step 2: Add "Departments" property to "EmployeeContext" class that is present in "EmployeeContext.cs" file in "Models" folder.
public class EmployeeContext : DbContext
{
   
public DbSet<Department> Departments { get; set; }
    
public DbSet<Employee> Employees { get; set; }
}

Step 3: Right click on the "Controllers" folder and add a Controller, with name=DepartmentController. Copy and paste the following code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVCDemo.Models;

namespace MVCDemo.Controllers
{
   
public class DepartmentController : Controller
    {
       
public ActionResult Index()
        {
           
EmployeeContext employeeContext = new EmployeeContext();
           
List<Department> departments = employeeContext.Departments.ToList();
           
return View(departments);
        }
    }
}

Step 4: Right click on the Index() action method in DepartmentController class and select "Add View" from the context menu. Set
View name = Index
View engine = Razor
Select "Create Strongly-typed view" checkbox
Select Department class, from "Model class" dropdownlist
Click "Add" button

Copy and paste the following code in Index.cshtml view file in Department folder
@using MVCDemo.Models;

@model IEnumerable<Department>

<div style="font-family:Arial">
@{
    ViewBag.Title = "Departments List";
}

<h2>Departments List</h2>
<ul>
@foreach (Department department in @Model)
{
    <li>@Html.ActionLink(department.Name, "Index", "Employee",
    new { departmentId = department.ID }, null)</li>
}
</ul>
</div>

Changes to Employee List and Detail pages
Add "DepartmentId" property to "Employee" model class that is present in Employee.cs file in "Models" folder.
[
Table("tblEmployee")]
public class Employee
{
   
public int EmployeeId { get; set; }
    
public string Name { get; set; }
    
public string Gender { get; set; }
   
public string City { get; set; }
   
public int DepartmentId { get; set; }
}

Add "departmentId" parameter to Index() action method in "EmployeeController" class that is present in "EmployeeController.cs" file in "Controllers" folder. Use the "departmentId" parameter to filter the list of employees as shown below.
public ActionResult Index(int departmentId)
{
   
EmployeeContext employeeContext = new EmployeeContext();
   
List<Employee> employees = employeeContext.Employees.Where(emp => emp.DepartmentId == departmentId).ToList();

   
return View(employees);
}

Copy and paste the following line in "Index.cshtml" that is present in "Employee" folder in "Views" folder. With this change we are able to generate an action link to redirect the user to a different controller action method.
@Html.ActionLink(
"Back to Department List", "Index", "Department")

Change the following line in "Details.cshtml" that is present in "Employee" folder in "Views" folder.
CHANGE THIS LINE @Html.ActionLink(
"Back to List", "Index")
TO
@Html.ActionLink(
"Back to Employee List", "Index", new { departmentId = @Model.DepartmentId }) 

Part 11 - Using business objects as model in mvc

Suggested Videos 
Part 8 - Data access in MVC using entity framework
Part 9 - Generate hyperlinks using actionlink html helper
Part 10 - Working with multiple tables in MVC



In this video, we will discuss using business objects as model. Until now, we have been using entity framework and entities. Entities are mapped to database tables, and object relational mapping tools like Entity Framework, nHibernate, etc are used to retrieve and save data. Business objects contain both state(data) and behaviour, that is logic specific to the business.



In MVC there are several conventions that needs to be followed. For example, controllers need to have the word controller in them and should implement IController interface either directly or indirectly. Views should be placed in a specific location that MVC can find them.

public class HomeController : Controller
{
   
public ViewResult Index()
    {
        ViewData[
"Countries"] = new List<string>()
        {
           
"India",
            "US",
            "UK",
            "Canada"
        };

       
return View();
    }
}

The following URL will invoke Index() action method with in the HomeController. Notice that our HomeController inherits from base Controller class which inturn inherits from ControllerBase class. ControllerBase inturn inherits from IController class.
http://localhost/MVCDemo/Home/Index

return View() statement with in the HomeController by default looks for a view with name = "Index" in "/Views/Home/" and "/Views/Shared/" folders. If a view with name = "Index" is not found, then, we get an error stating
The view 'Index' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Home/Index.aspx
~/Views/Home/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx
~/Views/Home/Index.cshtml
~/Views/Home/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml

But with models, there are no strict rules. Infact "Models" folder is optional and they can live anywhere. They can even be present in a separate project.

Let's now turn our attention to using business objects as model. We will be using table "tblEmployee" for this demo. Use the sql script to create and populate this table.

Create table tblEmployee
(
 Id
int Primary Key Identity(1,1),
 Name
nvarchar(50),
 Gender 
nvarchar(10),
 City 
nvarchar(50),
 DateOfBirth
DateTime
)

Insert into tblEmployee values('Mark','Male','London','01/05/1979')
Insert into tblEmployee values('John','Male','Chennai','03/07/1981')
Insert into tblEmployee values('Mary','Female','New York','02/04/1978')
Insert into tblEmployee values('Mike','Male','Sydeny','02/03/1974')
Insert into tblEmployee values('Scott','Male','London','04/06/1972')

Stored procedure to retrieve data 
Create procedure spGetAllEmployees
as
Begin
 Select Id, Name, Gender, City, DateOfBirth
 
from tblEmployee
End

Step 1: Create an ASP.NET MVC 4 Web application with name = MVCDemo

Step 2: Add a Class Library project with Name="BusinessLayer"

Step 3: Right click on the BusinessLayer class library project, and add a class file with name = Employee.cs.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BusinessLayer
{
   
public class Employee
    {
       
public int ID { get; set; }
       
public string Name { get; set; }
        
public string Gender { get; set; }
        
public string City { get; set; }
       
public DateTime DateOfBirth { get; set; }
    }
}

Step 4: Right click on the "References" folder of the "BusinessLayer" class library project, and add a reference to "System.Configuration" assembly.

Step 5: Right click on the BusinessLayer class library project, and add a class file with name = EmployeeBusinessLayer.cs.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;

namespace BusinessLayer
{
   
public class EmployeeBusinessLayer
    {
       
public IEnumerable<Employee> Employees
        {
           
get
            {
               
string connectionString =
                   
ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;

               
List<Employee> employees = new List<Employee>();

               
using (SqlConnection con = new SqlConnection(connectionString))
                {
                   
SqlCommand cmd = new SqlCommand("spGetAllEmployees", con);
                    cmd.CommandType =
CommandType.StoredProcedure;
                    con.Open();
                   
SqlDataReader rdr = cmd.ExecuteReader();
                   
while (rdr.Read())
                    {
                       
Employee employee = new Employee();
                        employee.ID =
Convert.ToInt32(rdr["Id"]);
                        employee.Name = rdr[
"Name"].ToString();
                        employee.Gender = rdr[
"Gender"].ToString();
                        employee.City = rdr[
"City"].ToString();
                        employee.DateOfBirth =
Convert.ToDateTime(rdr["DateOfBirth"]);

                        employees.Add(employee);
                    }
                }

               
return employees;
            }
        }
    }
}

Step 6: Right click on the "References" folder of the "MVCDemo" project, and add a reference to "BusinessLayer" project.

Step 7: Include a connection string with name = "DBCS" in Web.Config file
 
<add name="DBCS" 
       
connectionString="server=.; database=Sample; integrated security=SSPI"
       
providerName="System.Data.SqlClient"/>

Step 8: Right click on the "Controllers" folder and add Controller with name = "EmployeeController.cs".
public class EmployeeController : Controller
{
   
public ActionResult Index()
    {
       
EmployeeBusinessLayer employeeBusinessLayer =
           
new EmployeeBusinessLayer();

       
List<Employee> employees = employeeBusinessLayer.Employees.ToList();
       
return View(employees);
    }
}

Step 9: Right click on the Index() action method in the "EmployeeController" class and select "Add View" from the context menu. Set
View name = Index
View engine = Razor
Select "Create a strongly-typed view" checkbox
Scaffold Template = List
Click "Add" button

Run the application and navigate to http://localhost/MVCDemo/Employee/Index. The output should be as shown below.
Using business objects as model in mvc

Part 12 - Creating a view to insert data using mvc

Suggested Videos 
Part 9 - Generate hyperlinks using actionlink html helper
Part 10 - Working with multiple tables in MVC
Part 11 - Using business objects as model in mvc



In this video we will discuss, creating a view to insert a new employee into the database table tblEmployee. Please watch Part 11, before proceeding with this video. 



We want to present the end user with a form as shown in the image below
https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2lBMJgGx5gLw_Xm62m5gdR5GIqxWqKvgdsQ9WB_6uahqmuAM7e72p29wdYPVbGlIRYWpEcNy4re1qVaqKEo7UJ5K28iEnYTao1cY5EA_lWcHXba8klnXNjrxsNkoq1J1XvZXW3mim8CiN/s1600/insert+data+using+mvc.png

Copy and paste the following "Create" action method, in EmployeeController class.
[
HttpGet]
public ActionResult Create()
{
   
return View();
}

Please note that, the method is decorated with "HttpGet" attribute. This makes this action method to respond only to the "GET" request.

Now let's add a "Create" view. To do this, right click on the "Create" action method and select "Add View" from the context menu. Set
1. View name = "Create"
2. View engine = "Razor"
3. Select "Create Strongly-typed view" checkbox
4. Select "Employee" class, from "Model class" dropdownlist
5. Scaffold Template = Create
6. Click "Add" button

At this point "Create.cshtml" view will be added in "Employee" folder. If you have the following "Scripts" section at the bottom of the view, please delete it. We will discuss about sections and scripts in a later video session.
@section Scripts {
    @Scripts.Render(
"~/bundles/jqueryval")
}

Run the application and navigate to the following URL
"http://localhost/MVCDemo/Employee/Index"

Click on "Create New" link. You will be naviaged to the following URL
"http://localhost/MVCDemo/Employee/Create"

A form with textboxes to add a new employee is rendered. For employee "Gender" it is ideal to have a dropdownlist instead of a text box. To achieve this, REPLACE THE FOLLOWING LINE
@Html.EditorFor(model => model.Gender)

WITH
@Html.DropDownList(
"Gender", new List<SelectListItem>
{
new SelectListItem { Text = "Male", Value="Male" },
new SelectListItem { Text = "Female", Value="Female" }
},
"Select Gender")

Run the application and notice that, a dropdownlist is now displayed for "Gender".

If you click on "Create" button, you will get an error message stating -
The resource cannot be found. This is because we don't have the "Create" controller action method that can handle HTTPPost request. We will discuss, fixing this in our next video.

Part 13 - FormCollection in mvc

Suggested Videos 
Part 10 - Working with multiple tables in MVC
Part 11 - Using business objects as model in mvc
Part 12 - Creating a view to insert data



In this video we will discuss using FormCollection object in mvc and it's purpose. Please watch Part 12, before proceeding with this video.

FormCollection class will automatically receive the posted form values in the controller action method, in key/value pairs. Keys & values can be accessed using key names or index.



We implemented the following "Create" view in Part 12
Create employee view in mvc

We can use the FormCollection to loop thru each key and it's value that is posted to the server.
[
HttpPost]
public
ActionResult Create(FormCollection formCollection)
{
   
if (ModelState.IsValid)
    {
       
foreach (string key in formCollection.AllKeys)
        {
            Response.Write(
"Key = " + key + "  ");
            Response.Write(
"Value = " + formCollection[key]);
            Response.Write(
"<br/>");
        }
    }
   
return View();
}

The output is as shown below
FormCollection in mvc

Create the following stored procedure to insert employee data into tblEmployee table
Create procedure spAddEmployee
@Name
nvarchar(50),
@Gender 
nvarchar (10),
@City 
nvarchar (50),
@DateOfBirth
DateTime
as  
Begin  
 Insert into tblEmployee (Name, Gender, City, DateOfBirth)
 
Values (@Name, @Gender, @City, @DateOfBirth)
End

Add the following method to EmployeeBusinessLayer.cs file.
public void AddEmmployee(Employee employee)
{
   
string connectionString =
           
ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;

   
using (SqlConnection con = new SqlConnection(connectionString))
    {
       
SqlCommand cmd = new SqlCommand("spAddEmployee", con);
        cmd.CommandType =
CommandType.StoredProcedure;

       
SqlParameter paramName = new SqlParameter();
        paramName.ParameterName =
"@Name";
        paramName.Value = employee.Name;
        cmd.Parameters.Add(paramName);

        
SqlParameter paramGender = new SqlParameter();
        paramGender.ParameterName =
"@Gender";
        paramGender.Value = employee.Gender;
        cmd.Parameters.Add(paramGender);

        
SqlParameter paramCity = new SqlParameter();
        paramCity.ParameterName =
"@City";
        paramCity.Value = employee.City;
        cmd.Parameters.Add(paramCity);

        
SqlParameter paramDateOfBirth = new SqlParameter();
        paramDateOfBirth.ParameterName =
"@DateOfBirth";
        paramDateOfBirth.Value = employee.DateOfBirth;
        cmd.Parameters.Add(paramDateOfBirth);

        con.Open();
        cmd.ExecuteNonQuery();
    }
}

To save form data, to a database table, copy and paste the following code in EmployeeController.cs file.
[
HttpPost]
public ActionResult Create(FormCollection formCollection)
{
   
Employee employee = new Employee();
   
// Retrieve form data using form collection
    employee.Name = formCollection[
"Name"];
    employee.Gender = formCollection[
"Gender"];
    employee.City = formCollection[
"City"];
    employee.DateOfBirth =
       
Convert.ToDateTime(formCollection["DateOfBirth"]);

   
EmployeeBusinessLayer employeeBusinessLayer =
       
new EmployeeBusinessLayer();

    employeeBusinessLayer.AddEmmployee(employee);
   
return RedirectToAction("Index");
}

Do we really have to write all the dirty code of retrieving data from FormCollection and assign it to the properties of "employee" object. The answer is no. This is the job of the modelbinder in MVC. We will discuss modelbinders in our next video.

Part 14 - Mapping asp.net request data to controller action simple parameter types

Suggested Videos 
Part 11 - Using business objects as model in mvc
Part 12 - Creating a view to insert data
Part 13 - FormCollection in mvc



In this video we will discuss mapping asp.net request data to controller action simple parameter types. Please watch Part 13, before proceeding with this video.



To save form data, to a database table, we used the following code in Part 13. From Part 13, it should be clear that, FormCollection class will automatically receive the posted form values in the controller action method.
[
HttpPost]
public ActionResult Create(FormCollection formCollection)
{
   
Employee employee = new Employee();
    // Retrieve form data using form collection
    employee.Name = formCollection[
"Name"];
    employee.Gender = formCollection[
"Gender"];
    employee.City = formCollection[
"City"];
    employee.DateOfBirth =
       
Convert.ToDateTime(formCollection["DateOfBirth"]);

   
EmployeeBusinessLayer employeeBusinessLayer =
       
new EmployeeBusinessLayer();

    employeeBusinessLayer.AddEmmployee(employee);
   
return RedirectToAction("Index");
}

The above controller "Create" action method can be re-written using simple types as shown below. Notice that, the create action method has got parameter names that match with the names of the form controls. To see the names of the form controls, right click on the browser and view page source. Model binder in mvc maps the values of these control, to the respective parameters.
[
HttpPost]
public ActionResult Create(string name, string gender, string city, DateTime dateOfBirth)
{
   
Employee employee = new Employee();
    employee.Name = name;
    employee.Gender = gender;
    employee.City = city;
    employee.DateOfBirth = dateOfBirth;

   
EmployeeBusinessLayer employeeBusinessLayer =
       
new EmployeeBusinessLayer();

    employeeBusinessLayer.AddEmmployee(employee);
   
return RedirectToAction("Index");
}

But do we really to do these mappings manually. The answer is no. In a later video session we will see how to automatically map the request data, without having to do it manually.

Please note that, the order of the parameters does not matter. What matters is the name of the parameter. If the parameter name is different from the form control name, then the form data will not be mapped as expected.

Part 15 - Updatemodel function in mvc

Suggested Videos 
Part 12 - Creating a view to insert data
Part 13 - FormCollection in mvc
Part 14 - Mapping asp.net request data to controller action simple parameter types

In this video we will dsicuss using UpdateModel() function. Please watch Part 14, before proceeding. 



Change the implementation of "Create" action method that is decorated with [HTTPPost] aatribute in "EmployeeController" as shown below.
[
HttpPost]
public ActionResult Create(Employee employee)
{
   
if (ModelState.IsValid)
    {
       
EmployeeBusinessLayer employeeBusinessLayer =
           
new EmployeeBusinessLayer();

        employeeBusinessLayer.AddEmmployee(employee);
       
return RedirectToAction("Index");
    }
   
return View();
}



Please note:
1. Model state is being checked using IsValid boolean property of ModelState object. We will discuss ModelState in a later video session.
2. Instead of passing the individual properties of "Employee" object as parameters to the "Create" action method, we are now passing the "Employee" object itself.
3. The "Employee" object is then handed over to AddEmployee() method of "EmployeeBusinessLayer" class, which takes the responsibility of saving the "Employee" object to the database table.
4. Upon saving the employee, the user is then redirected to the "Index" action method.
5. If there are any "Model" validation errors, ModelState.IsValid returns false. In this case, we stay on the same create view, which gives the opportunity to correct the errors and resubmit the page.

The above method can be rewritten as shown below.
[
HttpPost]
public ActionResult Create()
{
   
if (ModelState.IsValid)
    {
       
EmployeeBusinessLayer employeeBusinessLayer =
           
new EmployeeBusinessLayer();

       
Employee employee = new Employee();
        UpdateModel<
Employee>(employee);

        employeeBusinessLayer.AddEmmployee(employee);
       
return RedirectToAction("Index");
    }
   
return View();
}

When you make this change, you get a compilation error stating -
Type 'MVCDemo.Controllers.EmployeeController' already defines a member called 'Create' with the same parameter types.Our intention here is to overload the "Create" controller action method based on "HttpGet" and "HttpPost". To fix this error, use "ActionName" attribute as shown below.
[
HttpGet]
[
ActionName("Create")]
public ActionResult Create_Get()
{
   
return View();
}

[
HttpPost]
[
ActionName("Create")]
public ActionResult Create_Post()
{
   
if (ModelState.IsValid)
    {
       
EmployeeBusinessLayer employeeBusinessLayer =
           
new EmployeeBusinessLayer();

       
Employee employee = new Employee();
        UpdateModel<
Employee>(employee);

        employeeBusinessLayer.AddEmmployee(employee);
       
return RedirectToAction("Index");
    }
   
return View();
}

Please Note:
1. We have changed the names of "Create" action methods to "Create_Get" and "Create_Post" depending on the actions they respond to.
2. "ActionName" is specified as "Create" for both of these methods. So, if a "GET" request is made to the "URL - http://localhost/MVCDemo/Employee/Create" then "Create_Get()" controller action method is invoked. On the other hand if a "POST" request is made to the same URL, then "Create_Post()" controller action method is invoked.
3. Instead of passing "Employee" object as a parameter to "Create_Post()" action method, we are creating an instance of an "Employee" object with in the function, and updating it using "UpdateModel()" function. "UpdateModel()" function inspects all the HttpRequest inputs such as posted Form data, QueryString, Cookies and Server variables and populate the employee object.

When you run the application, you may get an intermittent error stating -
Adding the specified count to the semaphore would cause it to exceed its maximum count. To fix this error, either
1. Restart IIS
OR
2. Disable connection pooling in the connection string of your web.config file

Part 16 - Difference between updatemodel and tryupdatemodel

Suggested Videos 
Part 13 - FormCollection in mvc
Part 14 - Mapping asp.net request data to controller action simple parameter types
Part 15 - Updatemodel function in MVC



In this video we will discuss the differences between updatemodel and tryupdatemodel functions. Please watch Part 15, before proceeding.



Make changes to "Create_Post()" controller action method as shown below.
[
HttpPost]
[
ActionName("Create")]
public ActionResult Create_Post()
{
   
EmployeeBusinessLayer employeeBusinessLayer =
       
new EmployeeBusinessLayer();

   
Employee employee = new Employee();
    UpdateModel(employee);
   
if (ModelState.IsValid)
    {
        employeeBusinessLayer.AddEmmployee(employee);
       
return RedirectToAction("Index");
    }
   
else
    {
       
return View();
    }
}

Please note that, "AddEmmployee()" method is now inside the "IF" condition that checks the validity of ModelState using ModelState.IsValid boolean property. Run the application and navigate to the following URL.
http://localhost/MVCDemo/Employee/Create

Submit the page without entering any data. You will get an error stating -
"The model of type 'BusinessLayer.Employee' could not be updated". This is because "DateOfBirth" property of "Employee" class is a non-nullable DateTime data type. DateTime is a value type, and needs to have value when we post the form. To make "DateOfBirth" optional change the data type to nullable DateTime as shown below.
public class Employee
{
   
public int ID { get; set; }
   
public string Name { get; set; }
    
public string Gender { get; set; }
    
public string City { get; set; }
   
public DateTime? DateOfBirth { get; set; }
}

Run the application and navigate to the following URL.
http://localhost/MVCDemo/Employee/Create

Submit the page without entering any data. You will now get a different error stating -
Procedure or function 'spAddEmployee' expects parameter '@Name', which was not supplied.

This is because, the following parameters of stored procedure "spAddEmployee" are all required.
@Name
@Gender
@City
@DateOfBirth

To make all these parameters optional, modify the stored procedure as shown below.
Alter procedure spAddEmployee  
@Name
nvarchar(50) = null,  
@Gender 
nvarchar(10) = null,   
@City 
nvarchar (50) = null,   
@DateOfBirth
DateTime  = null
as    
Begin    
 Insert into tblEmployee (Name, Gender, City, DateOfBirth)  
 
Values (@Name, @Gender, @City, @DateOfBirth)  
End

Run the application and navigate to the following URL.
http://localhost/MVCDemo/Employee/Create

Submit the page without entering any data. You will now get a different error stating -
Object cannot be cast from DBNull to other types.

To fix this error, make changes to "Employees" property in "EmployeeBusinessLayer.cs" file as shown below. Notice that we are populating "DateOfBirth" property of "Employee" object only if "DateOfBirth" column value is not "DBNull".
public IEnumerable<Employee> Employees
{
   
get
    {
       
string connectionString =
           
ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;

       
List<Employee> employees = new List<Employee>();

       
using (SqlConnection con = new SqlConnection(connectionString))
        {
           
SqlCommand cmd = new SqlCommand("spGetAllEmployees", con);
            cmd.CommandType =
CommandType.StoredProcedure;
            con.Open();
           
SqlDataReader rdr = cmd.ExecuteReader();
           
while (rdr.Read())
            {
               
Employee employee = new Employee();
                employee.ID =
Convert.ToInt32(rdr["Id"]);
                employee.Name = rdr[
"Name"].ToString();
                employee.Gender = rdr[
"Gender"].ToString();
                employee.City = rdr[
"City"].ToString();
               
if (!(rdr["DateOfBirth"] is DBNull))
                {
                    employee.DateOfBirth =
Convert.ToDateTime(rdr["DateOfBirth"]);
                }

                employees.Add(employee);
            }
        }

       
return employees;
    }
}

Run the application and navigate to the following URL
http://localhost/MVCDemo/Employee/Create

Submit the page without entering any data. Notice that a blank employee row is inserted into tblEmployee table.

Now let's make the following properties of "Employee" class required.
Name
City
DateOfBirth

To achieve this we can use "Required" attribute that is present in System.ComponentModel.DataAnnotations namespace. To use this namespace, BusinessLayer project need a reference to "EntityFramework" assembly. The changes to the "Employee" class are shown below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;

namespace BusinessLayer
{
   
public class Employee
    {
       
public int ID { get; set; }
        [
Required]
       
public string Name { get; set; }
       
public string Gender { get; set; }
        [
Required]
       
public string City { get; set; }
        [
Required]
       
public DateTime? DateOfBirth { get; set; }
    }
}

Run the application and navigate to the following URL.
http://localhost/MVCDemo/Employee/Create

Submit the page without entering any data. We now get an error stating -
The model of type 'BusinessLayer.Employee' could not be updated. Notice that this error is thrown when UpdateModel() function is invoked.

Now let's use TryUpdateModel() instead of UpdateModel(). Make changes to "Create_Post()" controller action method in "EmployeeController" as shown below.
[
HttpPost]
[
ActionName("Create")]
public ActionResult Create_Post()
{
   
EmployeeBusinessLayer employeeBusinessLayer =
       
new EmployeeBusinessLayer();

   
Employee employee = new Employee();
    TryUpdateModel(employee);
   
if (ModelState.IsValid)
    {
        employeeBusinessLayer.AddEmmployee(employee);
       
return RedirectToAction("Index");
    }
   
else
    {
       
return View();
    }
}

Run the application and navigate to the following URL
http://localhost/MVCDemo/Employee/Create

Submit the page without entering any data. Notice that, we don't get an exception now and the user remains on "Create" view and the validation errors are displayed to the user.

So, the difference is UpdateModel() throws an exception if validation fails, where as TryUpdateModel() will never throw an exception. The similarity is, both the functions are used to update the Model with the Form values and perform the validations.

Is it mandatory to use "UpdateModel()" or "Try"UpdateModel()" function to update the Model?
The answer is NO.

The above method can be re-written as shown below and we get the same behaviour.
[
HttpPost]
[
ActionName("Create")]
public ActionResult Create_Post(Employee employee)
{
   
EmployeeBusinessLayer employeeBusinessLayer =
       
new EmployeeBusinessLayer();

   
if (ModelState.IsValid)
    {
        employeeBusinessLayer.AddEmmployee(employee);
       
return RedirectToAction("Index");
    }
   
else
    {
       
return View();
    }
}

So the next question is, Why do we need to explicitly invoke model binding?
If you want to limit on what can be bound, explicitly invoking model binding can be very useful. We will discuss more about this in a later video session.

Part 17 - Editing a model in mvc

Suggested Videos 
Part 14 - Mapping asp.net request data to controller action simple parameter types
Part 15 - Updatemodel function in MVC
Part 16 - Difference between UpdateModel and TryUpdateModel



In this video we will discuss editing a model in mvc. Please watch Part 16, before proceeding.



Step 1: Copy and paste the following "Edit" controller action method in "EmployeeController.cs" file.
[
HttpGet]
public ActionResult Edit(int id)
{
   
EmployeeBusinessLayer employeeBusinessLayer =
           
new EmployeeBusinessLayer();
   
Employee employee =
           employeeBusinessLayer.Employees.Single(emp => emp.ID == id);
         
   
return View(employee);
}

Please note:
1. This method is decorated with [
HttpGet] attribute. So this method only responds to HTTP get request when editing data.
2. The "Edit" action method also receives "id" of the employee that is being edited. This "id" is used to retrieve the employee details.
3. The employee object is passed to the view

Step 2: Add "Edit" view
a) Right click on the "Edit" controller action method, and select "Add view" from the context menu
b) Set
    View name = Edit
    View engine = Razor
    Select "Create a strongly-typed view" check box
    Model class = "Employee"
    Scaffold template = "Edit"
    Finally click "Add" button
c) This should add "Edit.cshtml" to "Employee" folder in "Views" foolder
d) Delete the following scripts section that is present at the bottom of "Edit.cshtml" view
@section Scripts 
{
    @Scripts.Render(
"~/bundles/jqueryval")
}

Run the application and navigate to
http://localhost/MVCDemo/Employee/Index. This page should list all the employees. Click on "Edit" link. The "Edit" page should display the details of the "Employee" being edited. Notice that, by default "textboxes" are used for editing. It is ideal to have a dropdownlist for gender rather than a textbox. To achieve this. make the following changes to "Edit.cshtml"

REPLACE THE FOLLOWING CODE
@Html.EditorFor(model => model.Gender)
@Html.ValidationMessageFor(model => model.Gender)

WITH
@Html.DropDownList(
"Gender", new List<SelectListItem>
    {
   
new SelectListItem { Text = "Male", Value="Male" },
   
new SelectListItem { Text = "Female", Value="Female" }
    },
"Select Gender")
@Html.ValidationMessageFor(model => model.Gender)

Run the application. Edit an employee, and notice that a DropDownList is used for gender as expected. Post the form by clicking on "Save" button. We will get an error stating -
The resource cannot be found. We will discuss fixing this in our next video.

Part 18 - Updating data in mvc

Suggested Videos 
Part 15 - Updatemodel function in MVC
Part 16 - Difference between UpdateModel and TryUpdateModel
Part 17 - Editing a model in mvc



In this video we will discuss updating data in mvc. Please watch Part 17, before proceeding.


Step 1: Create a stored procedure to update employee data.
Create procedure spSaveEmployee    
@Id
int,
@Name
nvarchar(50),    
@Gender 
nvarchar (10),    
@City 
nvarchar (50),    
@DateOfBirth
DateTime 
as      
Begin      
 Update tblEmployee Set
 Name = @Name,
 Gender = @Gender,
 City = @City,
 DateOfBirth = @DateOfBirth
 
Where Id = @Id
End



Step 2: Add the following "SaveEmployee()" method to "EmployeeBusinessLayer" class in "BusinessLayer" project. This method is used to save employee data to the database table.
public void SaveEmmployee(Employee employee)
{
   
string connectionString =
           
ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;

   
using (SqlConnection con = new SqlConnection(connectionString))
    {
       
SqlCommand cmd = new SqlCommand("spSaveEmployee", con);
        cmd.CommandType =
CommandType.StoredProcedure;

       
SqlParameter paramId = new SqlParameter();
        paramId.ParameterName =
"@Id";
        paramId.Value = employee.ID;
        cmd.Parameters.Add(paramId);

        
SqlParameter paramName = new SqlParameter();
        paramName.ParameterName =
"@Name";
        paramName.Value = employee.Name;
        cmd.Parameters.Add(paramName);

        
SqlParameter paramGender = new SqlParameter();
        paramGender.ParameterName =
"@Gender";
        paramGender.Value = employee.Gender;
        cmd.Parameters.Add(paramGender);

        
SqlParameter paramCity = new SqlParameter();
        paramCity.ParameterName =
"@City";
        paramCity.Value = employee.City;
        cmd.Parameters.Add(paramCity);

        
SqlParameter paramDateOfBirth = new SqlParameter();
        paramDateOfBirth.ParameterName =
"@DateOfBirth";
        paramDateOfBirth.Value = employee.DateOfBirth;
        cmd.Parameters.Add(paramDateOfBirth);

        con.Open();
        cmd.ExecuteNonQuery();
    }
}

Step 3: Copy and paste the following "Edit" controller action method in "EmployeeController.cs" file.
[
HttpPost]
public ActionResult Edit(Employee employee)
{
   
if (ModelState.IsValid)
    {
       
EmployeeBusinessLayer employeeBusinessLayer =
           
new EmployeeBusinessLayer();
        employeeBusinessLayer.SaveEmmployee(employee);

       
return RedirectToAction("Index");
    }
   
return View(employee);
}

Please note:
1. This method is decorated with [
HttpPost] attribute. So this method only responds to HTTP post request when updating data.
2. The "Edit" action method receives the modified "Employee" object. This object is then passed to SaveEmployee() method, which saves the employee details. After the employee details are saved, the user is redirected to "Index" action.
3. If there are model validation errors, none of the code in the IF block gets executed. In this case, the user stays on the "Edit" view. Since we are passing "Employee" object to the "Edit" view, the user gets to see the validation errors. This allows him to fix those errors and re-submit the view.

Part 19 - Unintended updates in mvc

Suggested Videos 
Part 16 - Difference between UpdateModel and TryUpdateModel
Part 17 - Editing a model in mvc
Part 18 - Updating data in mvc



In this video we will discuss, how, unintended updates can happen in mvc. Please watch Part 18, before proceeding. Let's understand this with an example.



At the moment, "Employee Edit" view can be used to change all of the following fields.
1. Name
2. Gender
3. City
4. DateOfBirth

Let's make "Name" non-editable. To achieve this
CHANGE THE FOLLOWING CODE IN EDIT.CSHTML
@Html.EditorFor(model => model.Name)

TO
@Html.DisplayFor(model => model.Name)
@Html.HiddenFor(model => model.Name)

Run the application and edit an employee. Notice that, Name of the employee is no longer rendered using a textbox. At this point we may think, that it is impossible for the user to change the name of the employee using "Edit" view. That is not true. Because of the way we have written our code, tools like Fiddler can be used to very easily change any properties of the "Employee" object.

Fiddler can be downloaded from the following URL
http://fiddler2.com/get-fiddler

After you have downloaded and installed fiddler, run fiddler, and navigate to the following URL
http://localhost/MVCDemo/Employee/Edit/1

In fiddler, in web sessions window, select the url. Under the "Inspectors" tab you can see Request headers and response. We will discuss more about fiddler in a later video session.

Now click on "Save" button on "Edit" view. Notice that, under "Web Sessions" in fiddler, another request is captured for the same URL - http://localhost/MVCDemo/Employee/Edit/1

Now, without using the browser, let' us see how to generate a post request using fiddler.
1. Click on "Composer" tab in fiddler
2. Drag and drop the following URL from "Web Sessions" window onto Composer window.
http://localhost/MVCDemo/Employee/Edit/1
3. In "Reques Body" under "Composer" tab, change "Name" of the employee to "XYZ"
4. Finally click "Execute" button on "Fiddler"

Now either query the database table, or navigate to "Index" view and notice that the employee name is changed to "XYZ".

In our next video, we will discuss preventing these type of un-intended updates.

Part 20 - Preventing unintended updates in mvc

Suggested Videos 
Part 17 - Editing a model
Part 18 - Updating data
Part 19 - Unintended updates



In this video we will discuss, preventing unintended updates in mvc. Please watch Part 19, before proceeding. 



Modify "Edit" controller action method that is decorated with [
HttpPost] attribute as shown below. This method is present in "EmployeeController.cs" file.
[
HttpPost]
[
ActionName("Edit")]
public ActionResult Edit_Post(int id)
{
   
EmployeeBusinessLayer employeeBusinessLayer = new EmployeeBusinessLayer();

   
Employee employee = employeeBusinessLayer.Employees.Single(x => x.ID == id);
    UpdateModel(employee,
new string[] { "ID", "Gender", "City", "DateOfBirth" });
         
   
if (ModelState.IsValid)
    {
        employeeBusinessLayer.SaveEmployee(employee);

       
return RedirectToAction("Index");
    }

   
return View(employee);
}

Please note:
1. The name of the method is changed from "Edit" to "Edit_Post"
2. The method is decorated with [
ActionName("Edit")] and [HttpPost] attributes. This indicates that, this method is going to respond to "Edit" action, when the form is posted to the server.
3. The "id" of the employee that is being edited, is passed as a parameter to this method.
4. Using the "id" parameter we load the employee details(Id, Name, Gender, City & DateOfBirth) from the database.
Employee employee = employeeBusinessLayer.Employees.Single(x => x.ID == id);
5. We then call UpdateModel() function. This should automatically update "Employee" object with data from the posted form. We are also passing a string array as the second parameter. This parameter specifies the list of model properties to update. This is also called as include list or white list. Notice that, we did not include "Name" property in the list. This means, even if the posted form data contains value for "Name" property, it will not be used to update the "Name" property of the "Employee" object.
UpdateModel(employee,
new string[] { "ID", "Gender", "City", "DateOfBirth" });

So, if we were to generate a post request using fiddler as we did in the previous session, "Name" property of the "Employee" object will not be updated.

Alternatively, to exclude properties from binding, we can specify the exclude list as shown below.
[
HttpPost]
[
ActionName("Edit")]
public ActionResult Edit_Post(int id)
{
   
EmployeeBusinessLayer employeeBusinessLayer = new EmployeeBusinessLayer();

   
Employee employee = employeeBusinessLayer.Employees.Single(x => x.ID == id);
    UpdateModel(employee,
null, null, new string[] { "Name" });

   
if (ModelState.IsValid)
    {
        employeeBusinessLayer.SaveEmployee(employee);

       
return RedirectToAction("Index");
    }

   
return View(employee);
}

Notice that we are using a different overloaded version of UpdateModel() function. We are passing "NULL" for "prefix" and "includeProperties" parameters.
UpdateModel<
TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties)

Part 21 - Including and excluding properties from model binding using bind attribute

Suggested Videos 
Part 18 - Updating data
Part 19 - Unintended updates
Part 20 - Preventing unintended updates



In this video we will discuss, including and excluding properties from model binding using BIND attribute. Please watch Part 20, before proceeding.

In part 20, we have seen how to include and exclude properties from model binding, by passing a string array to UpdateModel() method. There is another way to do the same thing using "Bind" attribute.



Modify "Edit_Post()" controller action method that is present in "EmployeeController.cs" file, as shown below.
[
HttpPost]
[
ActionName("Edit")]
public
ActionResult Edit_Post([Bind(Include = "Id, Gender, City, DateOfBirth")] Employee employee)
{
   
EmployeeBusinessLayer employeeBusinessLayer = new EmployeeBusinessLayer();
    employee.Name = employeeBusinessLayer.Employees.Single(x => x.ID == employee.ID).Name;

   
if (ModelState.IsValid)
    {
        employeeBusinessLayer.SaveEmployee(employee);

       
return RedirectToAction("Index");
    }

   
return View(employee);
}

Notice that, we are using "BIND" attribute and specifying the properties that we want to include in model binding. Since, "Name" property is not specified in the INCLUDE list, it will be excluded from model binding.
public ActionResult Edit_Post([Bind(Include = "Id, Gender, City, DateOfBirth")] Employee employee)

At this point, run the application and navigate to
"http://localhost/MVCDemo/Employee/Edit/1". Click "Save" button, you will get a "Model" validation error stating - "The Name field is required".

This is because, we marked "Name" property in "Employee" class with "Required" attribute. Remove the "Required" attribute from "Name" property.
public class Employee
{
   
public int ID { get; set; }
   
public string Name { get; set; }
    [
Required]
    
public string Gender { get; set; }
    [
Required]
    
public string City { get; set; }
    [
Required]
   
public DateTime? DateOfBirth { get; set; }
}

So, if we were to generate a post request using fiddler as we did in the previous session, "Name" property of the "Employee" object will not be updated.

Alternatively, to exclude properties from binding, we can specify the exclude list using "Bind" attribute as shown below.
[
HttpPost]
[
ActionName("Edit")]
public ActionResult Edit_Post([Bind(Exclude = "Name")] Employee employee)
{
   
// Rest of the method implementation remains the same
}

Part 22 - Including and excluding properties from model binding using interfaces

Suggested Videos 
Part 19 - Unintended updates
Part 20 - Preventing unintended updates
Part 21 - Including and excluding properties from model binding using bind attribute



In this video we will discuss, including and excluding properties from model binding using interfaces. Please watch Part 21, before proceeding.

In part 20, we have seen how to include and exclude properties from model binding, by passing a string array to UpdateModel() method, and in part 21 we have seen achieving the same using "BIND" attribute.



To include and exclude properties from model binding using interfaces
Step 1: Create an interface "IEmployee" as shown below. Notice that this interface, has got only the properties that we want to include in model binding. "Name" property is not present. This means, "Name" property will be excluded from model binding. Copy and paste this code in "Employee.cs" class file in "BusinessLayer" project
public interface IEmployee
{
   
int ID { get; set; }
    
string Gender { get; set; }
   
string City { get; set; }
   
DateTime? DateOfBirth { get; set; }
}

Step 2: Make "Employee" class inherit from IEmployee interface
public class Employee : IEmployee
{
   
public int ID { get; set; }
   
public string Name { get; set; }
    [
Required]
    
public string Gender { get; set; }
    [
Required]
    
public string City { get; set; }
    [
Required]
   
public DateTime? DateOfBirth { get; set; }
}

Step 3: Modify "Edit_Post()" controller action method that is present in "EmployeeController.cs" file, as shown below.
[
HttpPost]
[
ActionName("Edit")]
public ActionResult Edit_Post(int id)
{
   
EmployeeBusinessLayer employeeBusinessLayer = new EmployeeBusinessLayer();
   
Employee employee = employeeBusinessLayer.Employees.Single(x => x.ID == id);
    UpdateModel<
IEmployee>(employee);

   
if (ModelState.IsValid)
    {
        employeeBusinessLayer.SaveEmmployee(employee);
        
return RedirectToAction("Index");
    }

   
return View(employee);
}

Notice that we are explicitly calling the model binder, by calling UpdateModel() function passing in our interface IEmployee. The model binder will update only the properties that are present in the interface.

So, if we were to generate a post request using fiddler as we did in the previous session, "Name" property of the "Employee" object will not be updated.

So, in short, there are several ways to include and exclude properties from Model Binding. Depending on the architecture and requirements of your project, you may choose the approach that best fit your needs.

Part 23 - Why deleting database records using get request is bad
Suggested Videos 
Part 20 - Preventing unintended updates
Part 21 - Including and excluding properties from model binding using bind attribute
Part 22 - Including and excluding properties from model binding using interfaces



In this video we will discuss, why deleting database records using GET request is bad. Please watch Part 22, before proceeding.

First let's discuss, how to delete data in MVC using GET request and then we will discuss, why it is bad to do so.



Step 1: Create a stored procedure to delete employee data by "ID"
Create procedure spDeleteEmployee
@Id
int
as
Begin
 Delete from tblEmployee
 
where Id = @Id
End

Step 2: Add the following DeleteEmployee() method to "EmployeeBusinessLayer.cs" file in "BusinessLayer" project. This method calls the stored procedure "spDeleteEmployee" that we just created.
public void DeleteEmployee(int id)
{
   
string connectionString =
           
ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;

   
using (SqlConnection con = new SqlConnection(connectionString))
    {
       
SqlCommand cmd = new SqlCommand("spDeleteEmployee", con);
        cmd.CommandType =
CommandType.StoredProcedure;

       
SqlParameter paramId = new SqlParameter();
        paramId.ParameterName =
"@Id";
        paramId.Value = id;
        cmd.Parameters.Add(paramId);

        con.Open();
        cmd.ExecuteNonQuery();
    }
}

Step 3: Add the following "DELETE" controller action method to "EmployeeController".
public ActionResult Delete(int id)
{
   
EmployeeBusinessLayer employeeBusinessLayer =
       
new EmployeeBusinessLayer();
    employeeBusinessLayer.DeleteEmployee(id);
   
return RedirectToAction("Index");
}

Run the application and navigate to "Index" action. Click the "Delete" link. This issues "GET" request to the following URL, and deletes the record.
http://localhost/MVCDemo/Employee/Delete/1

Deleting database records using GET request opens a security hole and is not recommended by Microsoft. Just imagine what can happen if there is an image tag in a malicious email as shown below. The moment we open the email, the image tries to load and issues a GET request, which would delete the data.
<img src="http://localhost/MVCDemo/Employee/Delete/2" />

Also, when search engines index your page, they issue a GET request which would delete the data. In general GET request should be free of any side-effects, meaning it should not change the state.

Deletes should always be performed using a POST request. We will discuss, implementing this in our next video.

No comments:

Post a Comment