我有以下通过一对多关系相关的模型类。这些类通过 Code First 方法持久化到 SQL Server 数据库中:
public class Topic
{
[Key]
public int Id { get; set; }
[InverseProperty("Topic")]
public virtual IList<Chapter> Chapters { get; set; }
//some other properties...
}
public class Chapter : IValidatableObject
{
[Key]
public int Id { get; set; }
[Required]
public string Key { get; set }
public virtual Topic Topic { get; set; }
//some other properties...
}
每个Topic
包含一堆Chapters
. 每个Chapter
都有一个Key
在其Topic
.
我试图用以下方法验证这一点:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var chaptersWithSameKey = Topic.Chapters.Where(t => t.Key == Key);
foreach (var item in chaptersWithSameKey)
{
if (item.Id != Id)
{
yield return new ValidationResult("The key must be unique.", new string[] { "Key" });
break;
}
}
}
但是,Topic
始终null
是在发布到“创建”或“编辑”操作后发生验证时。这似乎是合理的,因为视图不包含有关Topic
. 但是,我可以在控制器中提取主题,因为主题的 id 是 URL 的一部分。
我的第一次尝试是在控制器中的 Post Create 操作的开头设置主题:
[HttpPost]
public ActionResult Create(int topicId, Chapter chapter)
{
var topic = db.Topics.Find(topicId);
if (topic == null)
return HttpNotFound();
chapter.Topic = topic;
if(ModelState.IsValid)
...
}
然而,本章的Validate
方法在控制器可以做任何事情之前被调用。因此,本章的主题又是null
。
另一种方法是通过以下方式告诉 Create 视图它属于哪个主题:
[HttpGet]
public ActionResult Create(int topicId)
{
var topic = ...
var newChapter = new Chapter() { Topic = topic };
return View(newChapter);
}
并在视图中设置一个隐藏字段:
@Html.HiddenFor(model => model.Topic)
@Html.HiddenFor(model => model.Topic.Id)
第一个null
像以前一样给出一个主题。这看起来很自然,因为渲染的隐藏字段的值只是主题的ToString()
结果。
第二个似乎试图验证主题,但由于缺少属性而失败。实际原因是NullReferenceException
当一个只读属性Topic
试图评估另一个null
属性时。我完全不知道为什么要访问只读属性。调用堆栈有一些Validate...
方法。
那么上述场景的最佳解决方案是什么?我正在尝试在模型中进行验证,但缺少一些可以在控制器中检索的必要值。
我可以为此任务创建一个包含 aint TopicId
而不是Topic Topic
. 但是我必须将每个属性和注释复制到视图模型或通过继承来完成。第一种方法似乎效率很低。
所以到目前为止,继承方法可能是最好的选择。但是有没有其他选项不需要引入额外的类型?