2

I'm trying to create a basic registration form that takes in an address with a state from a drop down list. I can't seem to extract the data from the drop down into my model that contains a state object. I've tried creating a custom model binder as shown below. When I debug the ModelState is valid, but the state is always null.

Models:

public class AccountInfo 
{
    public virtual Guid accoundID { get; set; }
    public virtual string city { get; set; }
    public virtual string email_address { get; set; }
    public virtual string fax_number { get; set; }
    public virtual string first_name { get; set; }
    public virtual string last_name { get; set; }
    public virtual string mailing_address { get; set; }
    public virtual string phone_number { get; set; }
    public virtual State state { get; set; }
    public virtual string zip_code { get; set; }
}
public class State
{
    public virtual int ID { get; set; }
    public virtual string text { get; set; }
    public virtual string value { get; set; }
}

Model Binder:

 public class StateModelBinder : IModelBinder
{
    private RepositoryDB Database;
    public object BindModel(
        ControllerContext controllerContext,
        ModelBindingContext bindingContext
        )
    {
        var key = bindingContext.ModelName + ".state";
        var value = bindingContext.ValueProvider.GetValue(key);
        if (value == null)
        {
            return null;
        }
        var result = new State();
        Database = new RepositoryDB(ConfigurationManager.
            ConnectionStrings["ConnectionString"].ConnectionString);

        try
        {
            var query = from s in Database.States
                        where s.value == value.ToString()
                        select new State()
                        {
                            text = s.text,
                            ID = s.ID,
                            value = s.value
                        };
            result = (State)query.ToList().ElementAt(0);        
        }
        catch (Exception ex)
        {
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex.Message);
            bindingContext.ModelState.SetModelValue(key, value);
        }


        return result;
    }

Controller:

[HttpPost]
    public ActionResult Index(AccountInfo accountModel)
    {
        try
        {
            if (ModelState.IsValid)
            {
                Database.Account.Add(accountModel);
                Database.SaveChanges();

                return Redirect(Url.Action("Success"));
            }
        }
        catch (Exception ex)
        {
            ModelState.AddModelError(String.Empty, ex);
        }
        // invalid info - return with error message         
        //repop select lists
        GetStates();
        return View(accountModel);
    }
public void GetStates()
    {
        IEnumerable<SelectListItem> states = from s in Database.States
                                                select new SelectListItem
                                                {
                                                    Text = s.text,
                                                    Value = s.value
                                                };

        ViewBag.state = states.ToList();
    }

View:

@Html.LabelFor(model => model.state, "*State")
@Html.DropDownListFor(model => model.state, (IEnumerable<SelectListItem>)ViewBag.state)
2
  • What are you doing? Why custom model binder? Why ViewBag? Why not view models? Commented Aug 1, 2012 at 16:30
  • I am trying to store the "State" (as in a mailing address) as an object inside the AccountInfo class. I'm populating the drop down with a linq query from a simple table on the database that has every possible state. Could you demonstrate how to use a viewmodel instead of my implementation? Commented Aug 1, 2012 at 17:25

1 Answer 1

3

Here is your problem:

@Html.DropDownListFor(model => model.state, (IEnumerable<SelectListItem>)ViewBag.state)

Binding to model.State cannot work because that is a complex type:

public class State
{
    public virtual int ID { get; set; }
    public virtual string text { get; set; }
    public virtual string value { get; set; }
}

If you think about it the only thing the form is going to post back is the value of the selected drop down item like "MN" (Minnesota) and not a full class. (You haven't serialized a "State" class to each drop down list item's value, just the primary key of each state hopefully) You need to make your state on the model be a string or int depending on what type that primary key is of your state like so:

public class AccountInfo 
{
    //...
    public virtual string/int State { get; set; }
    //...
}

From there you can re-hydrate your State object if you'd like from the unique identifier of a state (which should be the value that is posted back to the server as a string or int for example)

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.