Julio Schurt
Julio Schurt

Reputation: 2104

MVC - Validation (Model.State) using ViewModel

I´m needing some help. I have the following scenario and I think that I doing something wrong.

- Model "State"

    namespace App.Model
    {
        public class State
        {

            [Key]
            [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
            public int idState { get; set; }

            [Required(ErrorMessage = "Initials is required")]
            public string StateInitials { get; set; }

            [Required(ErrorMessage = "Name is required")]
            public string StateName { get; set; }

            [Display(Name = "Update Date")]
            [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
            public DateTime? UpdateDate { get; set; }

            [Display(Name = "Update Responsible")]
            public string UpdateResponsible { get; set; }

        } //class

    } //namespace

- Model "Location"

    namespace App.Model
    {
        public class Location
        {

            [Key]
            [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
            public int idLocation { get; set; }

            public int idState { get; set; }

            [Required(ErrorMessage = "Name is required")]
            public string LocationName { get; set; }

            public string Status { get; set; }
            public string ManagerName { get; set; }
            public string Address { get; set; }

            [Display(Name = "Update Date")]
            [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
            public DateTime? UpdateDate { get; set; }

            [Display(Name = "Update Responsible")]
            public string UpdateResponsible { get; set; }

        } //class

    } //namespace

The relation between State and Location is one to many, but I didn´t describe this on model (using navigation fields).

I have a view where I want to edit the locations. To do that, I´m using the following view model.

- View Model "LocationsViewModel"

    namespace App.ViewModel
    {
        public class LocationsViewModel
        {

            public State objState { get; set; }

            public List<Location> lstLocations { get; set; }

        } //class

    } //namespace

To edit the Locations I use the following controller.

namespace App.Controllers
{

  public class LocationController : Controller
  {

    private DbContext db = new DbContext();

    // GET: /Location/Edit/5
    public ActionResult Edit(int? id)
    {

        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }

        State objState = db.States.Find(id);

        if (objState == null)
        {
            return HttpNotFound();
        }

        LocationsViewModel model = new LocationsViewModel();
        model.objState = objState;
        model.lstLocations = getLocations(objState.idState);  //I didn´t show this method here just to simplify

        return View(model);

    } //Edit

    // POST: /Location/Edit/5
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Editar(LocationsViewModel model)
    {

        State objState = db.States.Find(model.objState.idState);

        try
        {

            if (ModelState.IsValid)
            {

                //Saving Locations
                foreach (Location obj in model.lstLocations)
                {

                    Location objLocation =  db.Locations.Find(obj.idLocation);
                    objLocation.LocationName = obj.LocationName;
                    objLocation.Status = obj.Status;
                    objLocation.ManagerName = obj.ManagerName;
                    objLocation.Address = obj.Address;

                    objLocation.UpdateDate = DateTime.Now;
                    objLocation.UpdateResponsible = User.Identity.Name;
                    db.Entry(objLocation).State = EntityState.Modified;

                    db.SaveChanges();

                } //foreach

                return RedirectToAction("Index");

            }

        }
        catch (Exception e)
        {
            ModelState.AddModelError("", e.Message);
        }

        model.objState = objState;
        model.lstLocations = getLocations(objState.idState);  //I didn´t show this method here just to simplify

        return View(model);

    } //Edit

} //class

} //namespace

The problem/question is:

I wrote this code to edit (save) the list of locations of a specific State. When I submit the "Edit" view, the MVC try to validade the list of Locations (lstLocations) and the State (objState) as well, but I want to validate only the list of locations.

Note 1. I need to pass to my Edit view both objects: objState and lstLocations. I need the objState object because I show some State´s properties to the user on page (view).

Note 2. I´m getting ModelState.IsValid = false because model.objLocation is not valid, but I don´t want to check objLocation (is not relevant for this view). I just want to check the list of Locations (lstLocation)

What is the best approach to achieve my gol? Am I doing something wrong? Need I to change my way of thinking?

Upvotes: 0

Views: 2490

Answers (2)

thmshd
thmshd

Reputation: 5847

In addition to the solution you've already chosen, I found the custom RequiredIfAttribute useful. Using that, you can control if something is required base upon another condition, e.g.:

...
public bool RequireLocationName {
   get {
       return !Addresses.Any();
   }
}

[RequiredIf("RequireLocationName", true)]
public bool LocationName { get; set; }

Upvotes: 0

Ian
Ian

Reputation: 748

You'll need two things. The first is to remove the object from the ModelState which you do not want to validate. The second is you need to put your code block that is to execute in a valid state within the if(ModelState.IsValid) block.

public ActionResult Edit(int? id)
{
    //don't validate this field
    ModelState.Remove("yourObject.property"); 

    if (ModelState.IsValid)
    {
        ...
    }
}

Upvotes: 2

Related Questions