Support us .Net Basics C# SQL ASP.NET Aarvi MVC Slides C# Programs Subscribe Download

Part 28 - Customizing the autogenerated edit view

Suggested Videos 
Part 25 - Insert update delete in mvc using entity framework
Part 26 - Customizing the autogenerated index view
Part 27 - Customizing the autogenerated create view



In this video we will discuss, customizing the auto-generated edit view. Please watch Part 27, before proceeding. 

If you want "Select Department" as the first item in the "Department" dropdownlist on "Edit" view, then, 
REPLACE THE FOLLOWING LINE
@Html.DropDownList("DepartmentId", String.Empty)

WITH
@Html.DropDownList("DepartmentId", "Select Department")



Notice that, a textbox is used for gender. It is ideal to have a dropdownlist for gender rather than a textbox. To achieve this, make the following changes to "Edit.cshtml" view.

REPLACE THE FOLLOWING CODE
@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")

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)

At this point, we will still be able to change "Name" property of the employee, using tools like fiddler. We discussed this in Part 19 of this video series. There are several ways to prevent "Name" property from being updated.
1. Use UpdateModel() function and pass include and exclude list as a parameter - Discussed in Part 20
2. Use Bind attribute - Discussed in Part 21
3. Use interfaces - Discussed in Part 22

Now, let's discuss using BIND attribute to prevent updating "Name" property using tools like fiddler. Along the way, I will demonstrate adding model validation errors dynamically.

Change the implementation of "Edit" controller action method, that responds to [HttpPost] request as shown below
[HttpPost]
public ActionResult Edit([Bind(Exclude="Name")] Employee employee)
{
    Employee employeeFromDB = db.Employees.Single(x => x.EmployeeId == employee.EmployeeId);

    employeeFromDB.EmployeeId = employee.EmployeeId;
    employeeFromDB.Gender = employee.Gender;
    employeeFromDB.City = employee.City;
    employeeFromDB.DepartmentId = employee.DepartmentId;
    employee.Name = employeeFromDB.Name;

    if (ModelState.IsValid)
    {
        db.ObjectStateManager.ChangeObjectState(employeeFromDB, EntityState.Modified);
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    ViewBag.DepartmentId = new SelectList(db.Departments, "Id", "Name", employee.DepartmentId);
    return View(employee);
}

Please note that, we have excluded "Name" property from model binding using "Bind" attribute. Even without BIND attribute, users will not be able to change the "NAME" of the employee, as we are copying only the required properties(Excluding NAME property) from "employee" object to "employeeFromDB" which in turn is persisted to the database. Since, I want to demonstrate adding model validation errors dynamically, let the attribute be there.

At this point if we run the application and click on "Save" button on "Edit" view, we get a validation error stating - The Name field is required. This is because "Name" property is decorated with [Required] attribute in "Employee.cs" file. To prevent the validation error, remove the [Required] attribute. 

The problem with this change is that, "Name" field on "Create" view is no longer mandatory. This means we will now be able to create a new employee, without NAME. To fix the "Create" view, let's add model validation errors dynamically. Change the implementation of "Create" controller action method that responds to [HttpPost] request as shown below.
[HttpPost]
public ActionResult Create(Employee employee)
{
    if (string.IsNullOrEmpty(employee.Name))
    {
        ModelState.AddModelError("Name", "The Name field is required.");
    }
            
    if (ModelState.IsValid)
    {
        db.Employees.AddObject(employee);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    ViewBag.DepartmentId = new SelectList(db.Departments, "Id", "Name", employee.DepartmentId);
    return View(employee);
}

13 comments:

  1. Hi venkat, I'm using MVC 5, why I can not resolve db.ObjectStateManager?

    ReplyDelete
    Replies
    1. public ActionResult Edit([Bind(Exclude = "Name")] Employee employee)
      {
      Employee employeeFromDB = db.Employees.Single(x => x.EmployeeId == employee.EmployeeId);

      employeeFromDB.EmployeeId = employee.EmployeeId;
      employeeFromDB.Gender = employee.Gender;
      employeeFromDB.City = employee.City;
      employeeFromDB.DepartmentId = employee.DepartmentId;
      employee.Name = employeeFromDB.Name;

      if (ModelState.IsValid)
      {
      db.Entry(employeeFromDB).State = EntityState.Modified;
      db.SaveChanges();
      return RedirectToAction("Index");
      }
      ViewBag.DepartmentId = new SelectList(db.Departments, "Id", "Name", employee.DepartmentId);
      return View(employee);
      }

      Delete
  2. Hi..
    i have replaced this
    @Html.EditorFor(model => model.Gender)

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

    But dropdownlist doesnt show the value of the selected record.....
    i m using entityframework 5.0 mvc4...
    plz help me

    ReplyDelete
    Replies
    1. Check all my comment in Part 8 about MVC4 vs, MVC 5 and you know exactly why you have these problems.

      Delete
    2. Please help me how to solve this issue ,I have searched so much but cannot finding any answer/solution on this, I am using
      MVC 4 and EF 5.0 with VS 2013

      Delete
    3. make sure that you are used the nvarchar type in the database for gender and no nchar because nchar is fixed-length which will reserve storage space for number of characters you specify even if you don't use up all that space.
      But nvarchar is variable-length which will only use up spaces for the characters you store. It will not reserve storage like char or nchar.

      Delete
  3. Hi Prahim,
    I am using entityframework 6.1 MVC 4
    Two questions:
    1. Is it true that in this version of Entity, we do not need to create additional Metadata Models as they exist already? Or they still get regenerated?
    2. I understand why @ 10:50 you remove the [Required], but does this not allow users to Create entry without Name? Could you please explain how to fix this?

    Thanks for all your Videos, very well explained!

    ReplyDelete
  4. Salam Sir Venkat, Why does the ObjectStateManager property not exist in my db context?

    ReplyDelete
    Replies
    1. I was having problems as well with ObjectStateManager not existing in my db context but I figured out a solution that works.

      Create a new partial class named EmployeeContext.cs in your project. The location doesn't matter but I put in in the 'Models' folder so it would be close to the auto generated 'EmployeeDataModel.Context.cs' file which contains the definition for the public partial class EmployeeContext:

      namespace MVCDemo.Models
      {
      using System;
      using System.Data.Entity;
      using System.Data.Entity.Infrastructure;

      public partial class EmployeeContext : DbContext
      {
      .....

      Open the file EmployeeContext.cs that you added to the project. By default, it should look similar to this:


      namespace MVCDemo
      {
      public class EmployeeContext
      {
      }
      }


      Do the following to this class:
      1. Make this class a partial EmployeeContext class
      2. Add the following using statements: System.Data.Entity and System.Data.Entity.Infrastructure
      3. Make this class inherit from DbContext
      4. Add the following method:
      public ObjectContext ObjectContext()
      {
      return (this as IObjectContextAdapter).ObjectContext;
      }

      Now return to the line you were having problems with and change db.ObjectStateManager.ChangeObjectState(employeeFromDB, EntityState.Modified);
      TO
      db.ObjectContext().ObjectStateManager.ChangeObjectState(employeeFromDB, EntityState.Modified);

      Notice that you will have a new method available off of db and it is db.ObjectContext() from there you can access all of ObjectContext in it's full glory!

      Hope this help!
      John

      Delete
    2. hi John,
      Thanks for posting, but I still have an error with the method that you suggest adding into EmployeeContext:

      public ObjectContext ObjectContext()
      {
      return (this as IObjectContextAdapter).ObjectContext;
      }

      Error: EmployeeContext.ObjectContext() is a 'method' but is used like a 'type'

      Delete
  5. Hi! i'm following your videos and awesome, sir i'm using vs13. i'm facing a error when i compile my code. the browser generates a error Compilation Error : public class _Page_Views_Employee_Index_cshtml : System.Web.Mvc.WebViewPage> {
    don't know why this error occurs, please help me

    ReplyDelete
  6. db.ObjectStateManager is still not resolved getting compile time error. How to fix this ?

    ReplyDelete
  7. for visual studio 2019

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit([Bind(Include = "EmployeeId,Gender,City,DepartmentId")] Employee employee)
    {
    Employee employeeFromDb = db.Employees.Single(x => x.EmployeeId == employee.EmployeeId);

    employeeFromDb.EmployeeId = employee.EmployeeId;
    employeeFromDb.Gender = employee.Gender;
    employeeFromDb.City = employee.City;
    employeeFromDb.DepartmentId = employee.DepartmentId;
    employee.Name = employeeFromDb.Name;

    if (ModelState.IsValid)
    {
    db.Entry(employeeFromDb).State = EntityState.Modified;
    db.SaveChanges();
    return RedirectToAction("Index");
    }
    ViewBag.DepartmentId = new SelectList(db.Departments, "Id", "Name", employee.DepartmentId);
    return View(employee);
    }

    ReplyDelete

It would be great if you can help share these free resources