我正在学习MVC并结合实体框架处理其无状态特性。我的问题是,是否有更优雅的方式来处理下面的场景?
我有两个POCO实体:
public class Contest
{
public long ID { get; set; }
.... .....
public ICollection<ContestPrize> Prizes { get; set; }
}
public class ContestPrize
{
public long ID { get; set; }
public Contest Contest { get; set; }
}
然后我有一个MVC视图,显示比赛和与之相关的所有奖品。在那个视图中,我有一个“创建奖”链接来创建一个新的奖品,将比赛的ID传递给ContestPrize控制器,如下所示:
@Html.ActionLink("Create Prize", "Create", "ContestPrizes", new { ContestId = Model.ID }, null)
ContestId作为隐藏字段保留在提交新奖项的表单中,以便我可以在创建时检索它。奖品控制器上的创建方法如下:
[HttpPost]
public ActionResult Create(int ParentID, ContestPrize ContestPrize)
{
if (ModelState.IsValid)
{
db.ContestPrizes.Add(ContestPrize);
db.SaveChanges();
return RedirectToAction("Index", "Contests", ParentID);
}
return View(ContestPrize);
}
这是我遇到一些我不喜欢的丑陋的地方。以下是问题:
ModelState.IsValid
测试失败。这是因为父模型的导航属性为null。它还没有填充。我可以跳过模型上的验证并直接进入数据库,但有没有办法填充它而不用下面的#2?Parent.Prizes.Add(ContestPrize)
或ContestPrize.Contest = Parent
。但是我真的不喜欢这个,因为当我拥有创建子记录所需的所有信息时,我正在进行额外的数据库搜索以读取父级。那么,有没有办法让Entity Framework只设置父级的ID并保存子级而不必检索父对象?或者......理想情况下,有一种方法可以使用MVC在创建时使用父对象预填充新模型,因为在进入创建视图之前我确实有父对象吗?传递ViewData中的父对象可能有多糟糕?
我可以通过重新加载父项来完成这项工作。但是如果可能的话我想避免它。
思想?
更新:将ContestID
添加到ContestPrize POCO,然后使用[ForeignKey("ContestID")]
将导航属性修改为@NSGaga建议完美无缺,并消除了一些杂乱的ParentID传递问题。新的POCO看起来像这样:
public class Contest
{
public long ID { get; set; }
.... .....
public ICollection<ContestPrize> Prizes { get; set; }
}
public class ContestPrize
{
public long ID { get; set; }
public long ContestID { get; set; }
[ForeignKey("ContestID")]
public Contest Contest { get; set; }
}
我可以消除传递的ParentID并在视图中设置为隐藏字段,因为MVC中的数据绑定会处理新实体上的ContestID(我仍然必须将id传递给初始视图,但这都是它只是从那里开出来的。) Controller上的新Create操作如下所示:
[HttpPost]
public ActionResult Create(ContestPrize ContestPrize)
{
if (ModelState.IsValid)
{
db.ContestPrizes.Add(ContestPrize);
db.SaveChanges();
return RedirectToAction("Index", "Contests", ContestPrize.ContestID);
}
return View(ContestPrize);
}
干净简洁,没有乱七八糟。我喜欢它。
答案 0 :(得分:3)
如果您正在使用'代码优先'(所有建议),那么您可以执行类似......
的操作public class ContestPrize
{
public long ID { get; set; }
public long ContestID { get; set; }
public Contest Contest { get; set; }
}
那应该创建(按惯例)并将ContestID映射到FK。在某些复杂的情况下,您可能需要在流畅的代码中指定该关系,但这通常就足够了。
然后你可以通过
ContestID
引用父母 - 填写 - 做Add
。
这是粗暴的,如果您需要更多信息,请告诉我。
答案 1 :(得分:1)
您可能不应该直接使用实体作为视图模型。我建议使用这样的东西:
// Entitiy Type
public class ContestPrize { ... }
// ViewModel Type
public class ContestPrizeViewModel { ... }
// In your controller
[HttpPost]
public ActionResult Create(int ParentID, ContestPrizeViewModel ContestPrize)
{
if (ModelState.IsValid)
{
var entity = db.Create<ContestPrize>();
entity.ContestID = ParentID;
entity.Blah = ContestPrize.Blah
db.ContestPrizes.Add(entity);
db.SaveChanges();
return RedirectToAction("Index", "Contests", ParentID);
}
return View(ContestPrize);
}
这使您可以轻松地将域逻辑与视图逻辑分开。它可能需要额外的代码,但它是一种更强大的工作方式。