An elegant way to do this is to create your own model binder and a separate class to hold Kind
and RecordId
as properties. You'll need to specify model binder for your class with an attribute, and you'll then be able to use the class as an action method parameter.
[ModelBinder (typeof (RecordIdentityBinder))]
public class RecordIdentity
{
// Your types may be different
public RecordKind Kind { get; set; }
public int RecordId { get; set; }
//TODO: replace this with your formatting algorithm
public override string ToString ()
{
return string.Format ("{0}_{1}", Kind, RecordId);
}
//TODO: replace this with your parsing algorithm
public static RecordIdentity Parse (string s)
{
string[] fragments = s.Split('_');
if (fragments.Length != 2)
return null;
// Your object creation may be different
return new MessagePartyIdentity {
Kind = (RecordKind) Enum.Parse (typeof (RecordKind), fragments [0]),
PartyID = int.Parse (fragments [1])
};
}
}
public class RecordIdentityBinder : IModelBinder
{
public object BindModel (ControllerContext controllerContext, ModelBindingContext bindingContext)
{
string key = bindingContext.ModelName;
ValueProviderResult value = bindingContext.ValueProvider.GetValue (key);
if (value == null || string.IsNullOrEmpty (value.AttemptedValue))
return null;
return RecordIdentity.Parse (value.AttemptedValue);
}
}
public class MyController : Controller
{
// ?identity=Foo_42 will become RecordIdentity { Kind = RecordKind.Foo, RecordId = 42 }
public ActionResult DoSomethingWith (RecordIdentity selectedIdentity)
{
// ...
}
}
In the view template code, you'll need to call Html.DropDownListFor()
with your composite property:
@Html.DropDownListFor(m => m.SelectedIdentity, Model.RowList)
For each value in RowList
or whatever you call your data source, make sure SelectListItem
's Value
property is set to you class' ToString()
output.
The name of object's RecordIdentity
-typed property specified in Html.DropDownListFor
should match action parameter name. Otherwise you'll need to specify dropdown name in Bind
attribute's Prefix
property as seen here:
public ActionResult DoSomethingWith ([Bind(Prefix="SelectedIdentity")] RecordIdentity identity)
{
// do something you need
}
Also note that it will also work if you want RecordIdentity
to be a property of form model or whatever class that you'd like to accept as a parameter for your action method. The binder will get called either way.
By following this approach, you make single class responsible for formatting and parsing which is largely invisible and provides an abstraction over actual fields. However I need to note that such problem may as well be caused by poor database or business entities organization. Maybe a better solution would be to provide each of the items you listed with its own unique native key.