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)
Here is your problem:
Binding to model.State cannot work because that is a complex type:
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:
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)