3

I have a class that requires another class to be specified, but I don't want the MVC ModelState validator to check whether the secondary model is valid. Is this possible?

Here's a brief overview:

My entities look something like this:

public class WidgetType
{
    public long Id { get; private set; }

    [Required]
    public string Name { get; set; }

    ...
}

public class Widget
{
    public long Id { get; private set; }

    [Required]
    public string Name { get; set; }

    [Required]
    public WidgetType WidgetType { get; set; }

    ...
}

I have them encapsulated in a WidgetViewModel class that I'm passing to/from the View like this:

public class WidgetViewModel
{
    public Widget Widget { get; set; }

    public ICollection<WidgetType> WidgetTypes
    {
        get
        {
            return _repository.GetWidgets();
        }
    }

    ...
}

My view looks something like this:

...
@Html.DropDownListFor( m => m.Widget.WidgetType.Id, new SelectList( new EquipmentViewModel().EquipmentTypes, "Id", "Name" ) )
...

All of this works except for validation. ModelState.IsValid is always false because "Widget.WidgetType.Name" is required. I need the user to select a WidgetType, but I don't want ModelState to be validated deeper than "Widget.WidgetType.Id" (which should be all that Widget needs for its foreign key?).

Is there a better way to do this? I feel like there should be some way to validate without recursively inspecting deeper into the properties, but I can't find it. What am I missing...?

Farray
  • 8,290
  • 3
  • 33
  • 37
  • 1
    This link may give you an idea of how to achieve partial validation. http://blog.stevensanderson.com/2010/02/19/partial-validation-in-aspnet-mvc-2/ – bmosh Jul 14 '11 at 23:24
  • @bmosh That looks promising. Will check it out @ the office. – Farray Jul 15 '11 at 01:34
  • @bmosh creating a `ActionFilterAttribute` did exactly what I wanted. If you post an answer, I'll accept it. Thanks. – Farray Jul 15 '11 at 18:51

3 Answers3

4
public class WidgetViewModel
{    
    [Required]
    public string Name { get; set; }

    [Required]
    public WidgetType WidgetTypeId { get; set; }

    public SelectList WidgetTypes 
    {
        get
        {
             //This should be popuplated in your controller or factory not in the view model
             retun new SelectList{ _repository.GetWidgets(),"Id","Name");

        }
   }
}

In your view

 @Html.DropDownListFor( m => m.WidgetTypeId, Model.WidgetTypes)

And in your controller

public ActionResult Create(WidgetViewModel model)
{
    Widget widget = new Widget{
         Name = model.Name,
         WidgetType = yourManager.GetWidgetTypeByID(model.WigetTypeId);
    };

    yourManager.Create(widget);

    //...
}
Gregoire
  • 24,219
  • 6
  • 46
  • 73
  • That makes a lot of sense, but is it still possible to use `ModelState` for validation that way? – Farray Jul 15 '11 at 00:25
  • @Farray: Yes the ModelState can be used as it will validate data based on your viewmodel class and not on the widget class – Gregoire Jul 15 '11 at 07:10
  • I ended up implementing a custom `ActionFilterAttribute` per bmosh' comment, but I also reworked part of my ViewModel & View according to your advice. Thanks for the help. – Farray Jul 15 '11 at 18:52
3

If all you need in your view is the WidgetID then you don't need to include the entire Widget in the WidgetViewModel. Just have property called WidgetID. View model classes should have only the data the is necessary for the view.

In the controller action method that is called when you submit the form, you can use the WidgetID to fetch the Widget object from the database if it is needed.

HitLikeAHammer
  • 2,679
  • 3
  • 37
  • 53
1

http://blog.stevensanderson.com/2010/02/19/partial-validation-in-aspnet-mvc-2/ gives an example of partial validation

bmosh
  • 176
  • 3