views:

388

answers:

4

My strongly typed View inherits from a "Person" object that is created with Linq to SQL. In my "Edit" View, i have to display of course old values:

<%= Html.TextBox("FirstName") %>

"FirstName" is NCHAR, so it need to be trimmed. So i ended up with:

<%= Html.TextBox("FirstName", Model.FirstName.Trim()) %>

and this works. But when form is submitted (after POST) and some errors occur, i need to show it again:

[AcceptVerbsAttribute(HttpVerbs.Post), Authorize(Roles = "office"), HandleError]
public ActionResult Edit(Models.Person person)
{
    if (!(_personService.ValidatePerson(person))) // Persona non valida
    { return View(person); }
}

If for some reason the user left the textbox "FirstName" blank, the resulting property Person.FirstName become null and Model.FirstName.Trim() throws an Exception (Object reference not set to an instance of an object).

Any way to modify the bind and have all string trimmed by default? Or any ideas to how fix this?

Update: seems confirmed to be a MVC 2 behaviour.. still looking for a good way to handle this. Actually using an extension method:

public static string TrimOrDefault(this string value)
{
    return value != null ? value.Trim() : string.Empty;
}
A: 

In Models.Person have the default value be String.Empty which should elminate the null error at least.

If you want to have it automated, then you can add that into your dbml file. There is a designer.cs file attached and each field has a getter and setter. you can add code in there if you wish.

you could also create a partial class based on your table and handle the trim within that. it means that if you make a change to the dbml file then you don't lose your changes.

if you're not using a dbml file then let us know.

EDIT

If you have a class called person in your dbml file then this will already be declared as partial.

create another class, in the same project and do something like the following;

public partial class Person
{
    public string NewName
    {
        get { return this._name.Trim(); }
    }
}

So from then on in you can use .NewName instead of name and it will come back trimmed. You can also add code in there to ensure it's not null, not red, not whatever and return the appropriate value.

griegs
Seems ok, but i need to know how to create the partial class. I'm really new to MVC and asp.net in general...can anyone point me to the right direction on how edit dbml file or create partial class?
Gremo
The designer.cs file is generated. Modifying it and then changing your dbml means all of your changes are gone.
jfar
yes, i said that! which is why i added the bit about a partial class.
griegs
Recommending editing the designer.cs file is pure insanity.
jfar
A: 

I'm not aware of an extension method that does this, if someone else does then please let me know.

I create a simple HTML helper for fields that COULD be null.

public static string TrimOrDefault(string value)
{
    return (value == null ? "" : value.Trim());
}

And then in your code you can use

<%= Html.TextBox("FirstName", Helpers.TrimOrDefault(Model.FirstName)) %>

It's re-usable for future nullable fields and reads easily.

Odd
Down voter. I trust you have a solution hence your downvote? This is actually an acceptable solution.
griegs
"Any way to modify the bind and have all string trimmed by default? "Your answer has nothing to do with model binding, only HTML output. You gave him the example code he is already using just using extention method instead of Trim() this time: <%= Html.TextBox("FirstName", Model.FirstName.Trim()) %>
jfar
"Or any ideas to how fix this?"The helper was a simple solution to deal with a nullable field.
Odd
+1  A: 

Something is odd here.

Model.FirstName shouldn't be null; the model binder is smart enough to set an empty input field ( textbox ) to "". Make sure your property names match up between your model and the textboxes your using.

Which version of MVC are you using? 1 or 2? I'm running a MVC 1 version in VS 2008 and the only way I can get FirstName to be null is by not including it in the form at all.

I could see if you initial GET Edit view threw this error and you had FirstName set to nullable in your dbml and database but since it is a Post this doesn't make sense to me right now. ;)

Update:

I've confirmed this:

With an empty form:

  • VS 2008 - Mvc 1 - FirstName = ""
  • VS 2010 - Mvc 2 - FirstName = null

Uh oh... Thats going to break a lot of code...

The Code:

View ( same for both ):

<% using (Html.BeginForm()) {%>

    <fieldset>
        <legend>Fields</legend>
        <p>
            <label for="FirstName">FirstName:</label>
            <%= Html.TextBox("FirstName", Model.FirstName) %>
            <%= Html.ValidationMessage("FirstName", "*") %>
        </p>
        <p>
            <label for="LastName">LastName:</label>
            <%= Html.TextBox("LastName", Model.LastName) %>
            <%= Html.ValidationMessage("LastName", "*") %>
        </p>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>

<% } %>

VS 2010 - Mvc 2

public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewData["Message"] = "Welcome to ASP.NET MVC!";

        return View();
    }

    public ActionResult About()
    {
        return View();
    }

    public ActionResult AddPerson()
    {
        var person = new Person();

        return View(person);
    }

    [HttpPost]
    public ActionResult AddPerson(Person person)
    {
        return View(person);
    }
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

VS 2008 - Mvc 1

public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewData["Message"] = "Welcome to ASP.NET MVC!";

        return View();
    }

    public ActionResult About()
    {
        return View();
    }


    public ActionResult AddPerson()
    {
        var person = new Person();

        return View(person);
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult AddPerson( Person person )
    {

        return View(person);
    }
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

No clean fix atm. The model binder is actually setting those properties to null. Set First and Last to "" in the constructor and exclude those properties from binding: [Bind(Exclude="FirstName, LastName")] they stay "".

Is this documented someplace?

jfar
I'm thinking along this idea. Have you debugged the person object as it is passed into the Edit method (i.e. before it hits the validation service) to see if the first name is null or empty at this point? Is the person object modified in the validation service and being set to null there? Are you adding validation errors to the Model State using Model.ModelState.AddModelError? If so are you also using Model.ModelState.SetModelValue (they work as a pair and neglecting SetModelValue triggers weird null errors).
Michael Gattuso
Debuggin shows that the person property e.g. "LastName" is null after submit (if i set it blank in the corresponding textbox), right after the first line on code. Names match. Version 2 vs 2010 beta 1. LastName i nchar(30) not null...Thanks
Gremo
here's more about the null vs string.Empty thing : http://stackoverflow.com/questions/1263563/modelbinding-for-empty-query-string-parameters-in-asp-net-mvc-2
Simon_Weaver