这是处理任何关系的完整解决方案。
首先将以下代码行放入 Application_Start 事件中:
ModelBinders.Binders.DefaultBinder = new CustomModelBinder();
现在您需要在应用程序的某处添加以下类:
public class CustomModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (bindingContext.ModelType.Namespace.EndsWith("Models.Entities") && !bindingContext.ModelType.IsEnum && value != null)
{
if (Utilities.IsInteger(value.AttemptedValue))
{
var repository = ServiceLocator.Current.GetInstance(typeof(IRepository<>).MakeGenericType(bindingContext.ModelType));
return repository.GetType().InvokeMember("GetByID", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public, null, repository, new object[] { Convert.ToInt32(value.AttemptedValue) });
}
else if (value.AttemptedValue == "")
return null;
}
return base.BindModel(controllerContext, bindingContext);
}
}
请注意,可能需要修改上述代码以满足您的需要。它有效地调用 IRepository().GetByID(???)。当绑定到 Models.Entities 命名空间中的任何实体并且具有整数值时,它将起作用。
现在对于视图,您还需要做另一项工作来修复 ASP.NET MVC 2 中的错误。默认情况下,SelectListItem 上的 Selected 属性被忽略,所以我想出了我自己的 DropDownListFor,它允许您传入选定的值。
public static class SelectExtensions
{
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string selectedValue, string optionLabel)
{
return DropDownListFor(helper, expression, selectList, selectedValue, optionLabel, null);
}
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string selectedValue, string optionLabel, object htmlAttributes)
{
return DropDownListHelper(helper, ExpressionHelper.GetExpressionText(expression), selectList, selectedValue, optionLabel, new RouteValueDictionary(htmlAttributes));
}
/// <summary>
/// This is almost identical to the one in ASP.NET MVC 2 however it removes the default values stuff so that the Selected property of the SelectListItem class actually works
/// </summary>
private static MvcHtmlString DropDownListHelper(HtmlHelper helper, string name, IEnumerable<SelectListItem> selectList, string selectedValue, string optionLabel, IDictionary<string, object> htmlAttributes)
{
name = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
// Convert each ListItem to an option tag
var listItemBuilder = new StringBuilder();
// Make optionLabel the first item that gets rendered
if (optionLabel != null)
listItemBuilder.AppendLine(ListItemToOption(new SelectListItem() { Text = optionLabel, Value = String.Empty, Selected = false }, selectedValue));
// Add the other options
foreach (var item in selectList)
{
listItemBuilder.AppendLine(ListItemToOption(item, selectedValue));
}
// Now add the select tag
var tag = new TagBuilder("select") { InnerHtml = listItemBuilder.ToString() };
tag.MergeAttributes(htmlAttributes);
tag.MergeAttribute("name", name, true);
tag.GenerateId(name);
// If there are any errors for a named field, we add the css attribute
ModelState modelState;
if (helper.ViewData.ModelState.TryGetValue(name, out modelState))
{
if (modelState.Errors.Count > 0)
tag.AddCssClass(HtmlHelper.ValidationInputCssClassName);
}
return tag.ToMvcHtmlString(TagRenderMode.Normal);
}
internal static string ListItemToOption(SelectListItem item, string selectedValue)
{
var tag = new TagBuilder("option") { InnerHtml = HttpUtility.HtmlEncode(item.Text) };
if (item.Value != null)
tag.Attributes["value"] = item.Value;
if ((!string.IsNullOrEmpty(selectedValue) && item.Value == selectedValue) || item.Selected)
tag.Attributes["selected"] = "selected";
return tag.ToString(TagRenderMode.Normal);
}
}
现在在你看来,你可以说:
<%= Html.DropDownListFor(m => m.User.Role, Model.Roles, Model.User.Role != null ? Model.User.Role.RoleID.ToString() : "", "-- Please Select --")%>
<%= Html.ValidationMessageFor(m => m.User.Role, "*")%>
当您在控制器中调用 TryUpdateModel 时,Role 属性将自动更新,您无需做任何额外的工作来连接它。虽然最初有很多代码,但我发现这种方法从长远来看可以节省大量代码。
希望这可以帮助。