不,支架有意这里unopinionated,因为有很多不同的方法可以处理这个问题,也许你只是想从一个选择列表中选择一个,也许你想要复选框,或者,你想实际添加/编辑相关的内联项目?最后一个,你会喜欢立刻发布所有内容或使用AJAX?
因此,框架不是为您选择,而是正确地将决策留给您,因为只有您知道您的应用应该建立。无论如何,依靠脚手架往往会咬你更多。他们只能在最基本和理想的情况下工作,并且当应用程序要求是基本或理想时?我甚至不打扰他们,宁愿只手动创建我的控制器/视图。它最终比处理脚手架和解决所有不适用的事情更快。因此,由于您正在寻找选择框(无论是单选还是多选),首先,我建议为您的实体创建视图模型。例如,Tip
:
public class TipViewModel
{
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Required]
public int? SelectedPartnerId { get; set; }
public IEnumerable<SelectListItem> PartnerChoices { get; set;}
[Required]
public int? SelectedBookId { get; set; }
public IEnumerable<SelectListItem> BookChoices { get; set; }
}
在这里,我已经添加了可空INT(使用可空允许它们最初未选定的,而不是仅仅设定为第一个选项)属性来跟踪选定Book
的id/Partner
,因为它没有出现,你在外键的实体上有明确的属性。这很好,但它不会让它稍微复杂一些,以至于稍后会看到保存关系。如果你确实有明确的外键属性,那么你应该在你的视图模型中镜像它们。
在你行动的GET版本
现在,你需要做类似如下:
public ActionResult Create()
{
var model = new TipViewModel();
PopulateChoices(model);
return View(model);
}
...
protected void PopulateChoices(TipViewModel model)
{
model.PartnerChoices = db.Partners.Select(m => new SelectListItem
{
Value = m.Id.ToString(),
Text = m.Name
});
model.BookChoices = db.Books.Select(m => new SelectListItem
{
Value = m.Id.ToString(),
Text = string.Format("{0} by {1}", m.Name, m.Author)
});
}
我抽象出来的代码填充这些选择列表,因为代码将使用多个遍及你的控制器。另外,我在Text
值上使用string.Format
来表明您可以对选择列表项的文本进行任何操作。而且,上面的代码显然会用于创建操作。这样做的编辑会相似,但略有不同:
public ActionResult Edit(int id)
{
var tip = db.Tips.Find(id);
if (tip == null)
{
return new HttpNotFoundResult();
}
var model = new TipViewModel
{
Name = tip.Name,
Description = tip.Description,
SelectedPartnerId = tip.Partner != null ? tip.Partner.Id : new int?(),
SelectedBookId = tip.Book != null ? tip.Book.Id : new int?()
}
PopulateChoices(model);
return View(model);
}
的主要区别是,你显然与现有的实例打交道,所以你需要从数据库中拉出。然后,您只需将实体中的数据映射到您的视图模型。由于您没有明确的外键属性,因此您必须执行一些额外的工作才能获取当前选定的值,否则您可以直接复制外键属性的值。另外,在这里,我只是做一个手动映射,但有第三方库可以使这个任务变得更简单(请参阅:AutoMapper)。
因此,您可以实现您的意见。一切都会像直接使用实体一样工作,你只需要进行一些修改即可。首先,您需要更改您的视图模型声明:
@model Namespace.To.TipViewModel
然后,添加选择列表中为您的两个相关属性:
@Html.DropDownListFor(m => m.SelectedPartnerId, Model.PartnerChoices)
...
@Html.DropDownListFor(m => m.SelectedBookId, Model.BookChoices)
乐趣发生在你的行动的POST版本。大部分的代码将保持从一开始的版本是相同的,但现在你将有一个if (ModelState.IsValid)
块:
[HttpPost]
public ActionResult Create(TipViewModel model)
{
if (ModelState.IsValid)
{
// map the data from model to your entity
var tip = new Tip
{
Name = model.Name,
Description = model.Description,
Partner = db.Partners.Find(model.SelectedPartnerId),
Book = db.Books.Find(model.SelectedBookId)
}
db.Tips.Add(tip);
db.SaveChanges();
return RedirectToAction("Index");
}
// Form has errors, repopulate choices and redisplay form
PopulateChoices(model);
return View(model);
}
编辑的版本,再次是相似的,除了你要映射到你现有的实例,例如:
tip.Name = model.Name;
tip.Description = model.Description;
tip.Partner = db.Partners.Find(model.SelectedPartnerId);
tip.Book = db.Books.Find(model.SelectedBookId);
这就是它的全部参考性能。在你的问题中,你的实体中实际上没有任何M2M或甚至是一对多的东西。一切都是一对一的,但如果你有一个集合属性,你需要稍微不同的处理。您的视图模型上仍需要一个属性来保存选定的值和可用的选项:
public List<int> SelectedFooIds { get; set; }
public IEnumerable<SelectListItem> FooChoices { get; set; }
填充选项也是相同的。选项是选项;如果您只选择一个或多个,则无关紧要。
映射到你的实体在你创建的行动将是不同的,虽然,因为你需要从数据库中选择所有选中的项目,并在你的实体设置您的收藏属性设置为:
var tip = new Tip
{
...
Foos = db.Foos.Where(m => model.SelectedFooIds.Contains(m.Id)),
}
而且,您需要更改编辑操作的GET和POST版本。对于GET,需要凝聚您的收藏属性到ID列表:
var model = new TipViewModel
{
...
SelectedFooIds = tip.Foos.Select(m => m.Id).ToList(),
}
并在编辑的版本,设置新的选择项:
tip.Foos = db.Foos.Where(m => model.SelectedFooIds.Contains(m.Id);
最后,在你的意见,你会使用ListBoxFor
而不是DropDownListFor
启用多选:
@Html.ListBoxFor(m => m.SelectedFooIds, Model.FooChoices)
+1 - downvote否定 http://blogs.msdn.com/b/mcsuk soldev /存档/ 2013/09/20 /管理实体 - 关系 - 与-MVC-scaffolding.aspx – InferOn 2014-09-10 18:03:16