ASP.NET MVC model validation breaks MVC rule?
Asked Answered
K

3

6

using ASP.NET MVC, I have a Model, to which I'm attaching attributes so that I can get use the MVC Model bound validation, but to doesn't this break the rule of MVC, where you are putting items that belong to the View, into the Model? I hope I don't come of as trying to be smart, I am however curious as to other's opinions.

public class Payments
{
    [DataType(DataType.Text)]
    [DisplayFormat(NullDisplayText="")]
    [Display(Name="Payment Id")]
    [Required(ErrorMessage="Required")]
    public int PaymentId { get; set; } //todo: make this into a dropdown

    [DataType(DataType.Text)]
    [Display(Name="Bill Name")]
    [Required(ErrorMessage = "Required")]
    public string PaymentName { get; set; }

    [DataType(DataType.Date)]
    [Display(Name="Date to Post Payment")]
    [Required(ErrorMessage = "Required")]
    public DateTime PaymentDate { get; set; }

    [DataType(DataType.Currency)]
    [Range(0, 922337203685477.5807)]
    [Required(ErrorMessage = "Required")]
    public double PaymentAmount { get; set; }
}
Kea answered 18/5, 2011 at 14:9 Comment(0)
A
10

Yes. Thats why you should use ViewModels.

Arbitrate answered 18/5, 2011 at 14:10 Comment(3)
I do not understand the word "ViewModel" in the MVC context. I understand "MVC" to sand for Model-View-Controller. And I understand "MVVM" to stand for Model-View-ViewModel. So, if we are talking about MVC, then why are ViewModels being talked about?Gravy
Yeah, this answer is incorrect and violates MVC as well... Model != EntityWaterfront
Its important the tailor answers and discussions to the context of the tech stack at hand. Models and ViewModels mean something when discussing ASP.NET MVC but not when reading Martin Fowler. The original concept of MVC is almost nothing like actual implementations used on the web across almost every language.Arbitrate
A
10

You can, but don't have to, put those validation attributes in your model.

But it's better to use a ViewModel:

public class PaymentsViewModel
{
    [DataType(DataType.Text)]
    [DisplayFormat(NullDisplayText="")]
    [Display(Name="Payment Id")]
    [Required(ErrorMessage="Required")]
    public int PaymentId { get; set; } //todo: make this into a dropdown

    [DataType(DataType.Text)]
    [Display(Name="Bill Name")]
    [Required(ErrorMessage = "Required")]
    public string PaymentName { get; set; }

    [DataType(DataType.Date)]
    [Display(Name="Date to Post Payment")]
    [Required(ErrorMessage = "Required")]
    public DateTime PaymentDate { get; set; }

    [DataType(DataType.Currency)]
    [Range(0, 922337203685477.5807)]
    [Required(ErrorMessage = "Required")]
    public double PaymentAmount { get; set; }
}

And in your View, instead of:

@model YourProject.Models.Payments

you use:

@model YourProject.Models.PaymentsViewModel

for validation.

Andi answered 18/5, 2011 at 14:19 Comment(4)
Where is the best place to put the ViewModel class? Lets say you have two solutions: MyProject.UI and MyProject.Models, and the Payment class is MyProjects.Models.Payment. Is it better to put the ViewModel in MyProject.UI.Models.PaymentViewModel or MyProject.Models.PaymentViewModel?Norwood
@WeekendWarrior: It doesn't really matter where to put it actually. I hava a habbit of making 2 folders: Models and ViewModels and putting them in there.Andi
What would your model look like? Seems rather like semantic arguments here, though admittedly a simple example.River
To be honest that so called ViewModel is accurate Model and could be build out of entity. BUT... Entity/TableRecord != ModelWaterfront
R
1

Does it violate MVC in the strict sense, yeah, probably. Are there times when there's no harm in violating that? Sure. However, there are mechanisms to help you out and keep the concerns separate.

You can use your persisted domain objects in the views and validate against them, but when your view starts getting complicated, ViewModels become a necessity. For dead simple domain models or view-only views (not edits/creates), I'll sometimes fudge a bit and send them into the view as part of a composite object:

class MyViewModel
{
    public MyDomainModel DomainObj;
    public int OtherViewInfo;
}

However, for create and edit scenarios, ViewModels are far better. They allow you total control over the data being sent to the view and from the view.

If you are using EF 4.1 and CodeFirst, then yes, you'll end up with some duplication of attributes and properties between the domain and the ViewModel. This is unavoidable but gives you the flexibility of doing different validations specific to the view.

I've found it useful to have one extra layer of protection in the controller when actually saving the domain object in case I missed some validation in the view:

public class MyController : Controller
{
    [HttpPost]
    public ActionResult Edit(int id, MyViewModel model)
    {
        try
        {
            ...Do stuff, check ModelState.IsValid...
            _context.SaveChanges()
        }
        catch (DbEntityValidationException dbEx)
        {
            // Catch any validation errors on the domain that weren't duplicated
            // in the viewmodel
            ModelState.AddModelError("Form", dbEx);
        }

        return View(model);
    }
}

The next question to ask is how you map between the domain model and the ViewModel. There are a number of strategies and tools out there - AutoMapper and ValueInjecter (yes, spelled wrong). Personally, I've been using ValueInjecter although if you setup a mapping layer, you can try both. I've found that neither will work in 100% of cases, or at least I could figure out how to do what I needed, but a mapping service makes it easy to define custom left-to-right maps.

Rations answered 18/5, 2011 at 14:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.