views:

1138

answers:

6

Can anyone give me a succinct definition of the role of ModelState in Asp.net MVC (or a link to one). In particular I need to know in what situations it is necessary or desirable to call ModelState.Clear().

Can anyone give me a succinct definition of the role of ModelState in Asp.net MVC (or a link to one). In particular I need to know in what situations it is necessary or desirable to call ModelState.Clear().

Bit open ended huh... sorry, I think it might help if tell you what I'm acutally doing:

I have an Action of Edit on a Controller called "Page". When I first see the form to change the Page's details everything loads up fine (binding to a "MyCmsPage" object). Then I click a button that generates a value for one of the MyCmsPage object's fields (MyCmsPage.SeoTitle). It generates fine and updates the object and I then return the action result with the newly modified page object and expect the relevant textbox (rendered using <%= Html.TextBox("seoTitle", page.SeoTitle)%>) to be updated ... but alas it displays the value from the old model that was loaded.

I've worked around it by using ModelState.Clear() but I need to know why / how it has worked so I'm not just doing it blindly.

PageController:

[AcceptVerbs("POST")]
    public ActionResult Edit(MyCmsPage page, string submitButton)
    {
        // add the seoTitle to the current page object
        page.GenerateSeoTitle();

        // why must I do this?
        ModelState.Clear();

        // return the modified page object
        return View(page);
    }

Aspx:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MyCmsPage>" %>
....
        <div class="c">
            <label for="seoTitle">
                Seo Title</label>
            <%= Html.TextBox("seoTitle", page.SeoTitle)%>
            <input type="submit" value="Generate Seo Title" name="submitButton" />
        </div>
+1  A: 

ModelState in MVC is used primarily to describe the state of a model object largely with relation to whether that object is valid or not. This tutorial should explain a lot.

Generally you should not need to clear the ModelState as it is maintained by the MVC engine for you. Clearing it manually might cause undesired results when trying to adhere to MVC validation best practises.

It seems that you are trying to set a default value for the title. This should be done when the model object is instantiated (domain layer somewhere or in the object itself - parameterless ctor), on the get action such that it goes down to the page the 1st time or completely on the client (via ajax or something) so that it appears as if the user entered it and it comes back with the posted forms collection. Some how your approach of adding this value on the receiving of a forms collection (in the POST action // Edit) is causing this bizarre behaviour that might result in a .Clear() appearing to work for you. Trust me - you don't want to be using the clear. Try one of the other ideas.

cottsak
Does help me to rethink my services layer a bit (groan but thx) but as with a lot of stuff on the net it leans heavily towards the viewpoint of using ModelState for validation.
Mr Grok
Added more information to the question to show why I'm particularly interested in ModelState.Clear() and the reason for my query
Mr Grok
+1  A: 

Well the ModelState basically holds the current State of the model in terms of validation, it holds

ModelErrorCollection: Represent the errors when the model try to bind the values. ex.

TryUpdateModel();
UpdateModel();

or like a parameter in the ActionResult

public ActionResult Create(Person person)

ValueProviderResult: Hold the details about the attempted bind to the model. ex. AttemptedValue, Culture, RawValue.

Clean() method must be use with caution because it can lead to unspected results. And you will lose some nice properties of the ModelState like AttemptedValue, this is used by MVC in the background to repopulate the form values in case of error.

ModelState["a"].Value.AttemptedValue
Omar
hmmm... That might be where I'm getting the issue by the looks of it. I've inspected the value of the Model.SeoTitle property and it has changed but the attempted value has not. Looks as if it's sticking the value in as if there is an error on the page even though there isn't one (have checked the ModelState Dictionary and there are no errors).
Mr Grok
A: 

Got it in the end. My Custom ModelBinder which was not being registered and does this :

var mymsPage = new MyCmsPage();

NameValueCollection frm = controllerContext.HttpContext.Request.Form;

myCmsPage.SeoTitle = (!String.IsNullOrEmpty(frm["seoTitle"])) ? frm["seoTitle"] : null;

So something that the default model binding was doing must have been causing the problem. Not sure what, but my problem is at least fixed now that my custom model binder is being registered.

Mr Grok
Well i have no experience with a custom ModelBinder, the default one fits my needs so far =).
Omar
A: 

If you want to clear a value for an individual field then I found the following technique useful.

ModelState.SetModelValue("Key", new ValueProviderResult(null, string.Empty, CultureInfo.InvariantCulture));

Note: Change "Key" to the name of the field that you want to reset.

Carl Saunders
A: 

I think is a bug in MVC. I struggled with this issue for hours today.

Given this:

public ViewResult SomeAction(SomeModel model) 
{
    model.SomeString = "some value";
    return View(model); 
}

The view renders with the original model, ignoring the changes. So I thought, maybe it does not like me using the same model, so I tried like this:

public ViewResult SomeAction(SomeModel model) 
{
    var newModel = new SomeModel { SomeString = "some value" };
    return View(newModel); 
}

And still the view renders with the original model. What's odd is, when I put a breakpoint in the view and examine the model, it has the changed value. But the response stream has the old values.

Eventually I discovered the same work around that you did:

public ViewResult SomeAction(SomeModel model) 
{
    var newModel = new SomeModel { SomeString = "some value" };
    ModelState.Clear();
    return View(model); 
}

Works as expected.

I don't think this is a "feature," is it?

Tim Scott
A: 

I had an instance where I wanted to update the model of a sumitted form, and did not want to 'Redirect To Action' for performanace reason. Previous values of hidden fields were being retained on my updated model - causing allsorts of issues!.

A few lines of code soon identified the elements within ModelState that I wanted to remove (after validation), so the new values were used in the form:-

while (ModelState.FirstOrDefault(ms => ms.Key.ToString().StartsWith("SearchResult")).Value != null) { ModelState.Remove(ModelState.FirstOrDefault(ms => ms.Key.ToString().StartsWith("SearchResult"))); }

stevieg