您好我有一个名为User的实体,其中包含2个名为UserName和Role的属性(它是对另一个名为Role的实体的引用)。我正在尝试从回发的表单更新UserName和RoleID。在我的回发操作中,我有以下代码:
var user = new User();
TryUpdateModel(user, "User", new string[] { "UserName, Role.RoleID" });
TryUpdateModel(user, new string[] { "User.UserName, User.Role.RoleID" });
但是,这些都不会更新Role.RoleID属性。如果我尝试以下内容:
TryUpdateModel(user, "User", new string[] { "UserName, Role" });
TryUpdateModel(user);
更新RoleID但同时验证RoleName属性。这就是为什么我试图更具体地说明要更新哪些属性,但我无法让任何第一个例子起作用。
如果有人可以提供帮助,我会很感激。感谢
答案 0 :(得分:1)
这是一个完整的解决方案,适用于任何关系。
首先在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属性将自动更新,您不必再做其他工作来连接它。虽然最初有很多代码,但我发现这种方法可以长期保存大量代码。
希望这有帮助。
答案 1 :(得分:0)
考虑两次调用TryUpdateModel,一次为User。一次为角色。然后将角色分配给用户。
var user = new User();
var role = new Role();
TryUpdateModel(user, new string[] { "UserName" });
TryUpdateModel(role, new string[] { "RoleID" });
user.Role = role;
看看是否有效。