C# 每次使用新的guid索引时,如何获取要在集合属性上呈现的验证消息?
在这个ASP.NETMVC4程序的例子中,我让一个用户填写有关赛马的详细信息。这场比赛有一个名字,还有一份参赛马的名单。每匹马都有名字和年龄 表单使用ajax和javascript允许用户动态添加和删除输入字段,然后在按下submit按钮时一次提交 为了让这个过程对我来说更简单,我使用了made byC# 每次使用新的guid索引时,如何获取要在集合属性上呈现的验证消息?,c#,asp.net-mvc,asp.net-mvc-4,C#,Asp.net Mvc,Asp.net Mvc 4,在这个ASP.NETMVC4程序的例子中,我让一个用户填写有关赛马的详细信息。这场比赛有一个名字,还有一份参赛马的名单。每匹马都有名字和年龄 表单使用ajax和javascript允许用户动态添加和删除输入字段,然后在按下submit按钮时一次提交 为了让这个过程对我来说更简单,我使用了made by public static MvcHtmlString EditorForMany(此HtmlHelper html,表达式,字符串htmlFieldName=null),其中TModel:cla
public static MvcHtmlString EditorForMany(此HtmlHelper html,表达式,字符串htmlFieldName=null),其中TModel:class
{
var items=expression.Compile()(html.ViewData.Model);
var sb=新的StringBuilder();
if(String.IsNullOrEmpty(htmlFieldName))
{
var prefix=html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix;
htmlFieldName=(prefix.Length>0?(prefix+):String.Empty)+ExpressionHelper.GetExpressionText(表达式);
}
foreach(项目中的var项目)
{
var dummy=new{Item=Item};
var guid=guid.NewGuid().ToString();
var memberExp=Expression.MakeMemberAccess(Expression.Constant(dummy)、dummy.GetType().GetProperty(“项”);
var singleItemExp=Expression.Lambda(memberExp,Expression.Parameters);
sb.Append(字符串格式(@“”,htmlFieldName,guid));
Append(html.EditorFor(singleItemExp,null,String.Format(“{0}[{1}]”,htmlFieldName,guid));
}
返回新的MvcHtmlString(sb.ToString());
}
虽然我不理解所有细节(请阅读博客文章),但我知道它将索引值更改为guid,而不是顺序整数。这允许我删除列表中间的项目,而不需要重新计算索引。
这是我的MCVE的其余代码
HomeController.cs
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
var model = new Race();
//start with one already filled in
model.HorsesInRace.Add(new Horse() { Name = "Scooby", Age = 10 });
return View(model);
}
[HttpPost]
public ActionResult Index(Race postedModel)
{
if (ModelState.IsValid)
//model is valid, redirect to another page
return RedirectToAction("ViewHorseListing");
else
//model is not valid, show the page again with validation errors
return View(postedModel);
}
[HttpGet]
public ActionResult AjaxMakeHorseEntry()
{
//new blank horse for ajax call
var model = new List<Horse>() { new Horse() };
return PartialView(model);
}
}
公共类HomeController:控制器
{
[HttpGet]
公共行动结果索引()
{
var模型=新种族();
//从一个已经填好的开始
model.HorsesInRace.Add(newhorse(){Name=“Scooby”,Age=10});
返回视图(模型);
}
[HttpPost]
公共行动结果索引(种族postedModel)
{
if(ModelState.IsValid)
//模型有效,请重定向到其他页面
返回重定向到操作(“查看列表”);
其他的
//模型无效,请再次显示带有验证错误的页面
返回视图(postedModel);
}
[HttpGet]
公共行动结果AjaxMakeHorseEntry()
{
//用于ajax调用的新blank horse
var model=newlist(){newhorse()};
返回局部视图(模型);
}
}
模型.cs
public class Race
{
public Race() { HorsesInRace = new List<Horse>(); }
[Display(Name = "Race Name"), Required]
public string RaceName { get; set; }
[Display(Name = "Horses In Race")]
public List<Horse> HorsesInRace { get; set; }
}
public class Horse
{
[Display(Name = "Horse's Name"), Required]
public string Name { get; set; }
[Display(Name = "Horse's Age"), Required]
public int Age { get; set; }
}
公共级比赛
{
public Race(){HorsesInRace=new List();}
[显示(名称=“比赛名称”),必填]
公共字符串名称{get;set;}
[显示(Name=“赛马”)]
公共列表horssinrace{get;set;}
}
公马
{
[显示(名称=“马名”),必填]
公共字符串名称{get;set;}
[显示(Name=“马龄”),必填]
公共整数{get;set;}
}
Index.cshtml
@model CollectionAjaxPosting.Models.Race
<h1>Race Details</h1>
@using (Html.BeginForm())
{
@Html.ValidationSummary()
<hr />
<div>
@Html.DisplayNameFor(x => x.RaceName)
@Html.EditorFor(x => x.RaceName)
@Html.ValidationMessageFor(x => x.RaceName)
</div>
<hr />
<div id="horse-listing">@Html.EditorForMany(x => x.HorsesInRace)</div>
<button id="btn-add-horse" type="button">Add New Horse</button>
<input type="submit" value="Enter Horses" />
}
<script type="text/javascript">
$(document).ready(function () {
//add button logic
$('#btn-add-horse').click(function () {
$.ajax({
url: '@Url.Action("AjaxMakeHorseEntry")',
cache: false,
method: 'GET',
success: function (html) {
$('#horse-listing').append(html);
}
})
});
//delete-horse buttons
$('#horse-listing').on('click', 'button.delete-horse', function () {
var horseEntryToRemove = $(this).closest('div.horse');
horseEntryToRemove.prev('input[type=hidden]').remove();
horseEntryToRemove.remove();
});
});
</script>
@model collectionaaxposting.Models.Race
比赛详情
@使用(Html.BeginForm())
{
@Html.ValidationSummary()
@DisplayNameFor(x=>x.RaceName)
@EditorFor(x=>x.RaceName)
@Html.ValidationMessageFor(x=>x.RaceName)
@EditorForMany(x=>x.HorsesInRace)
添加新马
}
$(文档).ready(函数(){
//添加按钮逻辑
$(“#btn添加马”)。单击(函数(){
$.ajax({
url:'@url.Action(“AjaxMakeHorseEntry”),
cache:false,
方法:“GET”,
成功:函数(html){
$(“#马列表”).append(html);
}
})
});
//删除骑马按钮
$(“#马列表”)。在('click','button.delete horse',函数(){
var horseEntryToRemove=$(this.closest('div.horse');
horsentrytoremove.prev('input[type=hidden]').remove();
horsentrytoremove.remove();
});
});
Views/Shared/EditorTemplates/Horse.cshtml
@model CollectionAjaxPosting.Models.Horse
<div class="horse">
<div>
@Html.DisplayNameFor(x => x.Name)
@Html.EditorFor(x => x.Name)
@Html.ValidationMessageFor(x => x.Name)
</div>
<div>
@Html.DisplayNameFor(x => x.Age)
@Html.EditorFor(x => x.Age)
@Html.ValidationMessageFor(x => x.Age)
</div>
<button type="button" class="delete-horse">Remove Horse</button>
<hr />
</div>
@model collection.Models.Horse
@DisplayNameFor(x=>x.Name)
@EditorFor(x=>x.Name)
@Html.ValidationMessageFor(x=>x.Name)
@DisplayNameFor(x=>x.Age)
@EditorFor(x=>x.Age)
@Html.ValidationMessageFor(x=>x.Age)
脱马
Views/Home/ajaxmakehorsentry.cshtml
@model IEnumerable<CollectionAjaxPosting.Models.Horse>
@Html.EditorForMany(x => x, "HorsesInRace")
@model IEnumerable
@EditorForMany(x=>x,“HorsesInRace”)
数据流使用此代码。一个人可以在页面上创建和删除任意多的条目,并且当表单提交时,所有输入的值都会被提供给action方法
但是,如果用户没有在马条目的[必需]
信息中输入,ModelState.IsValid
将为false,再次显示表单,但是不会显示马属性的验证消息。不过,验证错误确实显示在ValidationSummary
列表中
例如,如果比赛名称
留空,同时显示一匹马的名称
,将显示前一匹的验证消息。后者将使用类“field validation valid”进行验证
我很确定这是因为每次创建页面时,EditorForMany
方法都会为每个属性创建新的guid,因此验证消息无法匹配到正确的字段
我能做些什么来解决这个问题?我是否需要放弃guid索引创建,或者是否可以更改EditorForMany
方法以允许验证消息
@model IEnumerable<CollectionAjaxPosting.Models.Horse>
@Html.EditorForMany(x => x, "HorsesInRace")
public static class HtmlHelperExtensions
{
public static MvcHtmlString EditorForMany<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, IEnumerable<TValue>>> propertyExpression, Expression<Func<TValue, string>> indexResolverExpression = null, string htmlFieldName = null) where TModel : class
{
htmlFieldName = htmlFieldName ?? ExpressionHelper.GetExpressionText(propertyExpression);
var items = propertyExpression.Compile()(html.ViewData.Model);
var htmlBuilder = new StringBuilder();
var htmlFieldNameWithPrefix = html.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName);
Func<TValue, string> indexResolver = null;
if (indexResolverExpression == null)
{
indexResolver = x => null;
}
else
{
indexResolver = indexResolverExpression.Compile();
}
foreach (var item in items)
{
var dummy = new { Item = item };
var guid = indexResolver(item);
var memberExp = Expression.MakeMemberAccess(Expression.Constant(dummy), dummy.GetType().GetProperty("Item"));
var singleItemExp = Expression.Lambda<Func<TModel, TValue>>(memberExp, propertyExpression.Parameters);
if (String.IsNullOrEmpty(guid))
{
guid = Guid.NewGuid().ToString();
}
else
{
guid = html.AttributeEncode(guid);
}
htmlBuilder.Append(String.Format(@"<input type=""hidden"" name=""{0}.Index"" value=""{1}"" />", htmlFieldNameWithPrefix, guid));
if (indexResolverExpression != null)
{
htmlBuilder.Append(String.Format(@"<input type=""hidden"" name=""{0}[{1}].{2}"" value=""{1}"" />", htmlFieldNameWithPrefix, guid, ExpressionHelper.GetExpressionText(indexResolverExpression)));
}
htmlBuilder.Append(html.EditorFor(singleItemExp, null, String.Format("{0}[{1}]", htmlFieldName, guid)));
}
return new MvcHtmlString(htmlBuilder.ToString());
}
}
public class Race
{
public Race() { HorsesInRace = new List<Horse>(); }
[Display(Name = "Race Name"), Required]
public string RaceName { get; set; }
[Display(Name = "Horses In Race")]
public List<Horse> HorsesInRace { get; set; }
}
public class Horse
{
[Display(Name = "Horse's Name"), Required]
public string Name { get; set; }
[Display(Name = "Horse's Age"), Required]
public int Age { get; set; }
// Note the addition of Index here.
public string Index { get; set; }
}
<div id="horse-listing">@Html.EditorForMany(x => x.HorsesInRace, x => x.Index)</div>
@Html.EditorForMany(x => x, x => x.Index, "HorsesInRace")
[HttpGet]
public ActionResult AjaxMakeHorseEntry()
{
var model = new Race();
model.HorsesInRace.Add(new Horse());
return PartialView(model);
}
@model Models.Race
@Html.EditorForMany(x => x.HorsesInRace, x => x.Index)