当前项目:
-
ASP。净额4.5.2
-
MVC五
-
实体框架6
-
Fluent验证
因此,我有一堆注释,它们是结构相同的表格,旨在与至少两页中的单个元素配对,并在第三页中进行总结。所有需要注释的元素都是单个循环的一部分,因此元素都是注释表挂起的同一个表的片段。例如,表示由循环表中的done(yes/no)布尔值和日期组成。演示说明是一个单独的表,仅用于这两个循环列,挂在循环表上(说明表有一个外键,它是循环的主键)。由于这些注释仅用于演示,因此整个注释表称为PresentationNotes。循环中还有许多其他元素有自己的Notes表,整个项目中的所有Notes表在结构上都是相同的。
从这个相同的结构中,我能够抽象出Model和View,这样我就不必为每个notes表复制不同的CRUD模型和CRUD视图。我在控制器中所要做的就是为每个notes表获取模型,并将特定条目与通用notes模型中的通用条目相关联。
例如,下面是上述演示模型:
namespace CCS.Models {
public class CycleNotesPresentation {
[Key]
public Guid NotesId { get; set; }
[DisplayName("Cycle")]
public Guid CycleId { get; set; }
[DisplayName("Comm. Type")]
public Guid NotesStatusId { get; set; }
[DisplayName("Date")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime NotesDate { get; set; }
[DisplayName("Notes")]
[DataType(DataType.MultilineText)]
public string Notes { get; set; }
#region Essentials
//Essential DB components for each and every table. Place at end.
[HiddenInput, DefaultValue(true)]
public bool Active { get; set; }
[HiddenInput, Timestamp, ConcurrencyCheck]
public byte[] RowVersion { get; set; }
[HiddenInput]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime Recorded { get; set; }
[HiddenInput]
public DateTime Modified { get; set; }
[HiddenInput]
public string TouchedBy { get; set; }
#endregion
[ForeignKey("CycleId")]
public virtual Cycle Cycle { get; set; }
[ForeignKey("NotesStatusId")]
public virtual NotesStatus NotesStatus { get; set; }
}
}
正如您所看到的,这里有很多东西不一定需要在抽象模型和视图中。
至少对于Create,抽象的Notes模型如下:
[Validator(typeof(CreateNotesValidator))]
public class CreateNotes {
public string NotesCategory { get; set; }
[DisplayName("Comm. Type")]
public string NotesStatusId { get; set; }
[DisplayName("Date")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime NotesDate { get; set; }
[DisplayName("Notes")]
public string Notes { get; set; }
}
当然,我还有另外三个模型:查看、编辑和删除,但现在让我们专注于这一个。如果我可以修复Create,我可以修复Edit,这是唯一一个需要客户端验证的下拉菜单。
注意上面的区别——
NotesStatusId
字段实际上是一个字符串,而不是Guid。事实证明,如果我一直使用Guid,那么我的客户端验证选项非常有限。另外,客户端验证仍然无法使用Guid,因此我决定使用字符串来简化模型(因此也简化了验证)。
因此,当我拉取原始的演示模型时,我将从Guid转换为字符串,当我处理Notes模型并将其转储回演示模型中时,我会将字符串转换回Guid。这允许我有更多的客户端验证选项。
我的控制员对整个过程是这样的:
// GET: Onboarding/CreateCycleNotesPresentation
[HttpGet]
public ActionResult CreateCycleNotesPresentation() {
var model = new CreateNotes() {
NotesCategory = "Presentation",
NotesDate = DateTime.Now
};
ViewBag.NotesStatusId = new SelectList(db.NotesStatus.Where(x => x.Active == true), "NotesStatusId", "NotesStatusName");
return PartialView("_CreateNotesPartial", model);
}
// POST: Onboarding/CreateCycleNotesPresentation
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> CreateCycleNotesPresentation(CreateNotes model) {
if(ModelState.IsValid) {
var id = new Guid(User.GetClaimValue("CWD-Cycle"));
CycleNotesPresentation cycleNotes = new CycleNotesPresentation();
cycleNotes.NotesId = new Guid();
cycleNotes.CycleId = id;
cycleNotes.NotesStatusId = new Guid(model.NotesStatusId);
cycleNotes.NotesDate = model.NotesDate;
cycleNotes.Notes = model.Notes;
cycleNotes.Active = true;
cycleNotes.Recorded = DateTime.UtcNow;
cycleNotes.Modified = DateTime.UtcNow;
cycleNotes.TouchedBy = User.Identity.GetFullNameLF();
db.CycleNotesPresentation.Add(cycleNotes);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
model.NotesCategory = "Presentation";
ViewBag.NotesStatusId = new SelectList(db.NotesStatus.Where(x => x.Active == true), "NotesStatusId", "NotesStatusName", model.NotesStatusId);
return PartialView("_CreateNotesPartial", model);
}
在这里,我们可以看到一些多汁的东西——我添加了一个
NotesCategory
条目,以便视图可以用要添加注释的元素的标题填充。这在最后不会被处理。
我还将通过刷新整个页面来结束POST。我发现这是最简单的解决方案,因为我无法使JSON提交正常工作(实际的POST方法从未收到数据,因此提交将挂起)。此外,整个页面的刷新效果更好。所以让我们别管这个了,好吗?
现在最重要的是:抽象Notes模型和视图的验证器:
namespace CCS.Validators {
class NotesValidator {
}
public class CreateNotesValidator : AbstractValidator<CreateNotes> {
public CreateNotesValidator() {
RuleFor(x => x.NotesDate)
.NotEmpty().WithMessage("Please select a date that this communication occurred on.");
RuleFor(x => x.NotesStatusId)
.NotEmpty().NotNull().WithMessage("Please indicate what type of communication occurred.");
RuleFor(x => x.Notes)
.NotEmpty().WithMessage("Please submit notes of some kind.")
.Length(2, 4000).WithMessage("Please provide notes of some substantial length.");
}
}
public class EditNotesValidator : AbstractValidator<EditNotes> {
public EditNotesValidator() {
RuleFor(x => x.NotesDate)
.NotEmpty().WithMessage("Please select a date that this communication occurred on.");
RuleFor(x => x.NotesStatusId)
.NotNull().NotEmpty().NotEqual("00000000-0000-0000-0000-000000000000").Matches("^[{(]?[0-9A-F]{8}[-]?([0-9A-F]{4}[-]?){3}[0-9A-F]{12}[)}]?$").WithMessage("Please indicate what type of communication occurred.");
RuleFor(x => x.Notes)
.NotEmpty().WithMessage("Please submit notes of some kind.")
.Length(2, 4000).WithMessage("Please provide notes of some substantial length.");
}
}
}
我们基本上可以忽略
EditNotesValidator
目前,因为这不是我们正在做的。
对于抽象的Notes,视图是一个简单的Partial,表单本身就像你能得到的一样:
@model CCS.Models.CreateNotes
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">Ã</button>
<h3 class="modal-title">Create Note for â@Model.NotesCategoryâ</h3>
</div>
@using(Html.BeginForm()) {
@Html.AntiForgeryToken()
<div class="modal-body">
<fieldset>
@Html.LabelFor(m => Model.NotesDate, new { @class = "control-label" })<div class="input-group date">@Html.TextBoxFor(m => m.NotesDate, "{0:yyyy-MM-dd}", new { @class = "form-control date" })<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span></div>
@Html.ValidationMessageFor(m => m.NotesDate)
@Html.LabelFor(m => Model.NotesStatusId, new { @class = "control-label" })@Html.DropDownList("NotesStatusId", null, "« ⹠Select ⺠»", htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.NotesStatusId)
@Html.LabelFor(m => Model.Notes, new { @class = "control-label" })@Html.TextAreaFor(m => m.Notes, new { @class = "form-control required" })
@Html.ValidationMessageFor(m => m.Notes)
</fieldset>
</div>
<div class="modal-footer">
<span id="progress" class="text-center" style="display: none;">
<img src="/images/wait.gif" alt="wait" />
Wait..
</span>
<button type="submit" value="Save" title="Save" class="btn btn-primary glyphicon glyphicon-floppy-disk"></button>
<button class="btn btn-warning" data-dismiss="modal">Close</button>
</div>
}
<script>
$("form").removeData("validator");
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
$(function () {
$.fn.datepicker.defaults.format = "yyyy-mm-dd";
$(".date").datepicker();
});
</script>
所以,是的。Date验证器完全按照预期工作。Notes文本区域得到了漂亮的验证。然而,下拉菜单已经完全过时了——无论我尝试什么,都可以
.NotEmpty()
或
.NotNull()
或者任何其他被FluentValidation明确标记为客户端功能的东西,下拉菜单都不起作用。对原始HTML的检查表明,我正在正确构建SelectList:
<select id="NotesStatusId" class="form-control" name="NotesStatusId">
<option value="">« ⹠Select ⺠»</option>
<option value="98e9f033-20df-e511-8265-14feb5fbeae8">Phone Call</option>
<option value="4899dd4d-20df-e511-8265-14feb5fbeae8">eMail</option>
<option value="8c073863-20df-e511-8265-14feb5fbeae8">Voice Mail</option>
<option value="8a13ec76-20df-e511-8265-14feb5fbeae8">Meeting</option>
</select>
默认值为空
« ⹠Select ⺠»
第一购股权
应该
意思是
.非空()
和
.NotNull()
应该
完美工作。但事实并非如此。如果我删除日期(它是自动填充表单加载的,请参阅上面的控制器),并保持下拉列表和文本区域不变,则只有日期字段和文本区域被标记——下拉列表根本没有被标记。
建议?
编辑1:
哎呀,oops现在添加了错误的控制器。
编辑2:
布勒?布勒?
编辑3:
我发现很难相信,没有其他人在通过FluentValidation对下拉菜单进行客户端验证时遇到过问题。