我有一个与我的模型有关的问题,你可以在下面的图片中看到。
正如你所看到的,我有3个实体和1:n和m:n之间的关系。
我希望我可以通过网络界面编辑这些模型。因此我支持(添加带有实体框架的控制器)这三个模型并获得edit/delete/create/
个视图,当然还有一个控制器用于每个实体。
但VS没有为关系自动创建输入/字段。所以我想手动实现它们。在我想要这样做之前有一种更简单的方法来实现/支撑这个模型,所以我甚至可以编辑关系(Checkboxes或(multi)select是最好的)? 提前谢谢!
答案 0 :(得分:0)
对于one-many,您可以在Partner View中使用DropDownList for Tip(参见Scott Allen's solution。许多 - 可以由ViewModels和JavaScript框架(如Knockout)处理。
答案 1 :(得分:0)
不,这里的脚手架是故意放置的,因为有很多不同的方法可以解决这个问题。也许您只想从选择列表中进行选择?也许你想要复选框?或者,您是否想要实际添加/编辑内联的相关项目?对于最后一个,你想一次发布所有内容还是使用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; }
}
在这里,我添加了nullable int(使用nullable允许它们最初未被选中,而不是仅设置为第一个选项)属性来跟踪所选Book
/ {{1的id因为它没有显示您的实体的外键具有显式属性。这没关系,但它并没有使得保存这种关系稍微复杂一些,正如你稍后会看到的那样。如果您确实具有显式外键属性,则应该在视图模型中镜像这些属性。
现在,在您的行动的GET版本中,您需要执行以下操作:
Partner
我已经抽象出用于填充这些选择列表的代码,因为代码将在整个控制器中多次使用。另外,我在书籍的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)
});
}
值上使用了string.Format
,以表明您可以使用选择列表项的文本执行任何操作。此外,显然,上面的代码将用于创建操作。进行编辑会有类似但略有不同:
Text
主要区别在于您显然正在处理现有实例,因此您需要从数据库中提取它。然后,您只需将实体中的数据映射到视图模型。由于您不具备明确的外键属性,因此您需要做一些额外的工作来获取当前选择的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);
}
/ Partner
值,否则您只需复制值直接用于外键属性。另外,在这里,我只是进行手动映射,但是有第三方库可以让这项工作更轻松(参见:AutoMapper)。
通过它,您可以实现您的视图。一切都将与您直接使用实体时的工作方式相同,您只需进行一些修改即可。首先,您需要更改视图的模型声明:
Book
然后,为您的两个相关属性添加选择列表:
@model Namespace.To.TipViewModel
乐趣发生在你的行动的POST版本中。大多数代码将与GET版本保持一致,但现在您将拥有@Html.DropDownListFor(m => m.SelectedPartnerId, Model.PartnerChoices)
...
@Html.DropDownListFor(m => m.SelectedBookId, Model.BookChoices)
块:
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);
}
这就是参考属性的全部内容。在你的问题中,你实际上没有任何关于你的实体的M2M甚至一对多的东西。一切都是一对一的,但如果你确实有一个收藏属性,你需要稍微区别对待它。您仍需要视图模型上的属性来保存所选值和可用选项:
tip.Name = model.Name;
tip.Description = model.Description;
tip.Partner = db.Partners.Find(model.SelectedPartnerId);
tip.Book = db.Books.Find(model.SelectedBookId);
填充选择也是一样的。选项是选项;如果您只选择一个或多个,那就不重要了。
在您的创建操作中映射到您的实体会有所不同,因为您需要从数据库中选择所有选定的项目并将您的实体上的集合属性设置为:
public List<int> SelectedFooIds { get; set; }
public IEnumerable<SelectListItem> FooChoices { get; set; }
并且,您需要更改编辑操作的GET和POST版本。对于GET,您需要将集合属性压缩为id列表:
var tip = new Tip
{
...
Foos = db.Foos.Where(m => model.SelectedFooIds.Contains(m.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