Asp.net MVC 模块绑定与复杂类型验证
我正在 asp.net mvc 中构建一个调查表应用程序,并且在使用模块绑定程序获取复杂类型列表时遇到问题。
第一:我无法获取 Questionnaire.IList
第二:我希望将我的存储库/工厂而不是类传递给活页夹,这样我就可以放弃表单问题和数据库问题之间的映射,而只处理一组对象。 或者至少比这段代码更漂亮一些;)。
有人为我指明了正确的方向吗?
控制器:
public class QuestionnaireController : Controller
{
#region Constructors
public QuestionnaireController(IRepositoryWithTypedId<QuestionGroup, string> questionnaireRepository)
{
repository = questionnaireRepository;
}
#endregion
public ActionResult Create(string Id)
{
if (!string.IsNullOrEmpty(Id))
{
QuestionGroup questionnaire = repository.Get(Id);
return View(questionnaire);
}
else return RedirectToAction("Index");
}
[Transaction]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(string Id, QuestionGroup questionGroup)
{
QuestionGroup dbQuestionGroup = repository.Get(Id);
//Map Questions in form to Questions from db
foreach (var question in dbQuestionGroup.Questions)
{
Question persQuestion =
(from item in questionGroup.Questions where item.QuestionID.Equals(question.ID) select item)
.SingleOrDefault();
if (persQuestion != null)
{
question.Answer = persQuestion.Answer;
}
}
//Validate Questions(db)
bool valid = true;
foreach (var question in dbQuestionGroup.Questions)
{
if (!question.IsValid())
{
foreach (var item in question.ValidationMessages)
{
ViewData.ModelState.AddModelError("questionGroup.Questions[" + question.ID + "]." +
item.PropertyName,
item.Message);
}
valid = false;
}
}
//Return the same form with validation info appended
if (!valid)
{
return View(dbQuestionGroup);
}
//Persist to db and redirect to Complete
else
{
repository.SaveOrUpdate(dbQuestionGroup);
return RedirectToAction("Complete");
}
}
private IRepositoryWithTypedId<QuestionGroup, string> repository;
}
部分视图(主视图仅对 QuestionGroup 中的问题执行 foreach 并为每个问题呈现此视图)
<div class="Question">
<div class="QuestionTitle">
<%=ViewData.Model.Description %>
</div>
<input name="questionGroup.Questions.Index" value='<%=ViewData.Model.ID %>' type="hidden" />
<input name='<%="questionGroup.Questions[" + ViewData.Model.ID + "].QuestionID" %>' value='<%=ViewData.Model.ID %>' type="hidden" />
<div class="QuestionText">
<%switch (ViewData.Model.Type.ToLower())
{
case QuestionType.Text:%>
<%=Html.TextBox("questionGroup.Questions[" + ViewData.Model.ID + "].Answer")%>
<% break;
case QuestionType.Number:%>
<%=Html.TextBox("questionGroup.Questions[" + ViewData.Model.ID + "].Answer")%>
<%= Html.ValidationMessage("questionGroup.Questions[" + ViewData.Model.ID + "].Answer")%>
<% break;
case QuestionType.PhoneNumber:%>
<%=Html.TextBox("questionGroup.Questions[" + ViewData.Model.ID + "].Answer")%>
<% break;
case QuestionType.Email:%>
<%=Html.TextBox("questionGroup.Questions[" + ViewData.Model.ID + "].Answer")%>
<%= Html.ValidationMessage("response.Questions[" + ViewData.Model.ID + "].Answer")%>
<% break;
case QuestionType.Date:%>
<%=Html.TextBox("questionGroup.Questions[" + ViewData.Model.ID + "].Answer")%>
<% break;
case QuestionType.YesNo:%>
<%=Html.RadioButton("questionGroup.Questions[" + ViewData.Model.ID + "].Answer", "0", true)%>Ikke valgt<br />
<%=Html.RadioButton("questionGroup.Questions[" + ViewData.Model.ID + "].Answer", "true", false)%>Ja<br />
<%=Html.RadioButton("questionGroup.Questions[" + ViewData.Model.ID + "].Answer", "false", false)%>Nei<br />
<% break;
case QuestionType.Alternative:%>
<%=Html.DropDownList("questionGroup.Questions[" + ViewData.Model.ID + "].Answer", new SelectList(ViewData.Model.Alternatives, "ID", "Description"))%>
<% break;
}%>
</div>
</div>
I'm building a questionnaire app in asp.net mvc, and I'm having trouble with the module binder for a list of complex types.
First: I can't get Questionnaire.IList<QuestionGroup>.IList<Question> object graph to work with the binder. So in the sample code below i use only one level IList.
Second: I would love to pass in my repository/factory instead of class to the binder, that way i could forgo the mapping between the form questions and the db questions, just working on one set of objects. Or at least something a bit more pretty than this code;).
Anyone got a pointer in the right direction for me?
The controller:
public class QuestionnaireController : Controller
{
#region Constructors
public QuestionnaireController(IRepositoryWithTypedId<QuestionGroup, string> questionnaireRepository)
{
repository = questionnaireRepository;
}
#endregion
public ActionResult Create(string Id)
{
if (!string.IsNullOrEmpty(Id))
{
QuestionGroup questionnaire = repository.Get(Id);
return View(questionnaire);
}
else return RedirectToAction("Index");
}
[Transaction]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(string Id, QuestionGroup questionGroup)
{
QuestionGroup dbQuestionGroup = repository.Get(Id);
//Map Questions in form to Questions from db
foreach (var question in dbQuestionGroup.Questions)
{
Question persQuestion =
(from item in questionGroup.Questions where item.QuestionID.Equals(question.ID) select item)
.SingleOrDefault();
if (persQuestion != null)
{
question.Answer = persQuestion.Answer;
}
}
//Validate Questions(db)
bool valid = true;
foreach (var question in dbQuestionGroup.Questions)
{
if (!question.IsValid())
{
foreach (var item in question.ValidationMessages)
{
ViewData.ModelState.AddModelError("questionGroup.Questions[" + question.ID + "]." +
item.PropertyName,
item.Message);
}
valid = false;
}
}
//Return the same form with validation info appended
if (!valid)
{
return View(dbQuestionGroup);
}
//Persist to db and redirect to Complete
else
{
repository.SaveOrUpdate(dbQuestionGroup);
return RedirectToAction("Complete");
}
}
private IRepositoryWithTypedId<QuestionGroup, string> repository;
}
The partial view (main view just does a foreach on Questions in QuestionGroup and renders this view for each Question)
<div class="Question">
<div class="QuestionTitle">
<%=ViewData.Model.Description %>
</div>
<input name="questionGroup.Questions.Index" value='<%=ViewData.Model.ID %>' type="hidden" />
<input name='<%="questionGroup.Questions[" + ViewData.Model.ID + "].QuestionID" %>' value='<%=ViewData.Model.ID %>' type="hidden" />
<div class="QuestionText">
<%switch (ViewData.Model.Type.ToLower())
{
case QuestionType.Text:%>
<%=Html.TextBox("questionGroup.Questions[" + ViewData.Model.ID + "].Answer")%>
<% break;
case QuestionType.Number:%>
<%=Html.TextBox("questionGroup.Questions[" + ViewData.Model.ID + "].Answer")%>
<%= Html.ValidationMessage("questionGroup.Questions[" + ViewData.Model.ID + "].Answer")%>
<% break;
case QuestionType.PhoneNumber:%>
<%=Html.TextBox("questionGroup.Questions[" + ViewData.Model.ID + "].Answer")%>
<% break;
case QuestionType.Email:%>
<%=Html.TextBox("questionGroup.Questions[" + ViewData.Model.ID + "].Answer")%>
<%= Html.ValidationMessage("response.Questions[" + ViewData.Model.ID + "].Answer")%>
<% break;
case QuestionType.Date:%>
<%=Html.TextBox("questionGroup.Questions[" + ViewData.Model.ID + "].Answer")%>
<% break;
case QuestionType.YesNo:%>
<%=Html.RadioButton("questionGroup.Questions[" + ViewData.Model.ID + "].Answer", "0", true)%>Ikke valgt<br />
<%=Html.RadioButton("questionGroup.Questions[" + ViewData.Model.ID + "].Answer", "true", false)%>Ja<br />
<%=Html.RadioButton("questionGroup.Questions[" + ViewData.Model.ID + "].Answer", "false", false)%>Nei<br />
<% break;
case QuestionType.Alternative:%>
<%=Html.DropDownList("questionGroup.Questions[" + ViewData.Model.ID + "].Answer", new SelectList(ViewData.Model.Alternatives, "ID", "Description"))%>
<% break;
}%>
</div>
</div>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我已经编写了如何使用 MvcContrib.FluentHtml 执行此操作:
我看到的一个问题是你将问题列表项的索引器值设置为调查问卷 ID,该 ID 向上两级。 该值应该是问题的 ID(如果存在),或者告诉您这是一个新实例的某个代理值(例如负数)。
I have written how to do this with MvcContrib.FluentHtml:
One problem I see is that you are setting the value of your indexer of your Question list item to the Questionaire ID, which is two levels up. The value should be the ID of the question, if it exists, or some proxy value (such as a negative number) that tells you it's a new instance.
您始终可以编写自己的自定义 ModelBinder,这需要您编写两个方法。 请参阅此处的 UpdateCollection 方法。 然后,您可以将存储库传递给自定义 ModelBinder。
You can always write your own custom ModelBinder which would require you to write about two methods. See the UpdateCollection method here. You could then passs your repository to your custom ModelBinder.