Asp.net mvc 在ASP.NETMVC中,将子对象添加到没有显式外键id字段且EF代码优先的父对象
我有两个模型类-Asp.net mvc 在ASP.NETMVC中,将子对象添加到没有显式外键id字段且EF代码优先的父对象,asp.net-mvc,entity-framework,ef-code-first,code-first,Asp.net Mvc,Entity Framework,Ef Code First,Code First,我有两个模型类-Parent和Child,它们仅通过键入的导航属性链接 public class Parent { [Key] [Required] public int Id { get; set; } [Required] public string ParentName { get; set; } public virtual ICollection<Child> Children { get; set; } } publi
Parent
和Child
,它们仅通过键入的导航属性链接
public class Parent {
[Key]
[Required]
public int Id { get; set; }
[Required]
public string ParentName { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
public class Child {
[Key]
[Required]
public int Id { get; set; }
[Required]
public string ChildName { get; set; }
[Required]
public virtual Parent Parent { get; set; }
}
用户填写表单后,使用HTTP POST将数据发送到Create
操作
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Child child)
{
if (ModelState.IsValid)
{
//db.Parents.Attach(child.Parent); //Added later
db.Children.Add(child);
db.SaveChanges();
return RedirectToAction("Index", new { parentId = child.Parent.Id });
}
}
这里我遇到了我的第一个问题。child.Parent
不是空的,child.Parent.Id
是正确的,但EF将其销毁,并在数据库中创建一个新的空父项(使用不同的键),并将该子项链接到它。我通过在添加子对象之前将子对象
(db.Parents.Attach(child.Parent)
)附加到数据上下文,解决了这个问题
但后来我遇到了另一个问题。起初,我的模型类是错误的,没有[Required]
属性,因此创建了可为空的数据库表列。我添加了属性,代码停止工作。代码不起作用,因为传递的子项的child.Parent.Name
为null
,因此ModelState.IsValid
为false
如何解决将子对象添加到父对象的问题?我对以下解决方案感兴趣:
- 首先使用EF代码和ASP.NETMVC
- 不涉及从数据库中获取
子.Parent
,只是为了让模型验证器满意
- 不涉及向模型添加显式外键(
ParentId
)
这可能吗?我认为尝试将父对象附加到子对象有点倒退。通常,您会将子对象附加到父对象。正在创建一个新的父项,很可能是因为您的子模型中没有包含父项id为的输入元素。因此,当子项绑定到POST时,父项id可能为null。EF看到了这一点,认为您也想创建一个新的父级
此外,由于parentId是路由的一部分,因此不需要在视图模型中指定它,除非您对视图中的Html.BeginForm()执行特殊操作。也就是说,如果您只使用Html.BeginForm,它将使用发送给GET请求的相同URL值发布
创建方法
public ActionResult Create(int parentId) {
var parent = db.Parents.Find(parentId);
if (parent == null) {
return HttpNotFound();
}
return View(new Child());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(int parentId, Child child)
{
if (ModelState.IsValid)
{
//Probably not a bad idea to check again...just to be sure.
//Especially since we are attaching a child to the parent object anyways.
var parent = db.Parents.Find(parentId);
if (parent == null) {
return HttpNotFound();
}
parent.Childern.Add(child);
db.SaveChanges();
return RedirectToAction("Index", new { parentId = parentid });
}
}
我认为试图把父母和孩子联系在一起有点倒退。通常,您会将子对象附加到父对象。正在创建一个新的父项,很可能是因为您的子模型中没有包含父项id为的输入元素。因此,当子项绑定到POST时,父项id可能为null。EF看到了这一点,认为您也想创建一个新的父级
此外,由于parentId是路由的一部分,因此不需要在视图模型中指定它,除非您对视图中的Html.BeginForm()执行特殊操作。也就是说,如果您只使用Html.BeginForm,它将使用发送给GET请求的相同URL值发布
创建方法
public ActionResult Create(int parentId) {
var parent = db.Parents.Find(parentId);
if (parent == null) {
return HttpNotFound();
}
return View(new Child());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(int parentId, Child child)
{
if (ModelState.IsValid)
{
//Probably not a bad idea to check again...just to be sure.
//Especially since we are attaching a child to the parent object anyways.
var parent = db.Parents.Find(parentId);
if (parent == null) {
return HttpNotFound();
}
parent.Childern.Add(child);
db.SaveChanges();
return RedirectToAction("Index", new { parentId = parentid });
}
}
是指向您问题完整答案的链接。简而言之,当您处理断开连接的实体时,EF将不考虑已设置的实体ID,并将整个实体图标记为新的(例如添加的)
我个人不喜欢它,只是过度使用了SaveChanges(),尽管当您有一个带有int(或long)Id属性的EnityBase基类(我觉得非常方便)时,它的工作原理如下
是指向您问题完整答案的链接。简而言之,当您处理断开连接的实体时,EF将不考虑已设置的实体ID,并将整个实体图标记为新的(例如添加的)
我个人不喜欢它,只是过度使用了SaveChanges(),尽管当您有一个带有int(或long)Id属性的EnityBase基类(我觉得非常方便)时,它的工作原理如下
我认为这应该适用于Create方法:
public ActionResult Create([Bind(Exclude="Parent")]Child child)
我认为这应该适用于Create方法:
public ActionResult Create([Bind(Exclude="Parent")]Child child)
公共类菜单
{
//公共菜单()
//{
// {
//this.Templates=newhashset();
// }
//}
[关键]
public int MenuId{get;set;}
[列(“菜单选项”)]
[显示(Name=“菜单标题”)]
[长度(100)]
公共字符串菜单选项{get;set;}
[显示(名称=“父菜单”)]
public int?ParentMenuId{get;set;}
公共虚拟菜单父菜单{get;set;}
[显示(Name=“Is Group”)]
公共bool IsGroup{get;set;}
[显示(Name=“菜单顺序”)]
公共整数菜单项{get;set;}
[显示(Name=“可见性”)]
公共布尔可见性{get;set;}
[显示(Name=“可见性主菜单”)]
公共布尔可视性m{get;set;}
[列(“控制器”)]
[显示(Name=“Controller”)]
[长度(100)]
公共字符串控制器{get;set;}
[栏目(“行动”)]
[显示(Name=“操作”)]
[长度(150)]
公共字符串操作{get;set;}
[显示(Name=“Icon”)]
公共int?IconID{get;set;}
[外键(“IconID”)]
公共虚拟图标{get;set;}
公共虚拟ICollection模板{get;set;}
}
公共类菜单
{
//公共菜单()
//{
// {
//this.Templates=newhashset();
// }
//}
[关键]
public int MenuId{get;set;}
[列(“菜单选项”)]
[显示(Name=“菜单标题”)]
[长度(100)]
公共字符串菜单选项{get;set;}
[显示(名称=“父菜单”)]
public int?ParentMenuId{get;set;}
公共虚拟菜单父菜单{get;set;}
[显示(Name=“Is Group”)]
公共bool IsGroup{get;set;}
[显示(Name=“菜单顺序”)]
公共整数菜单项{get;set;}
[显示(Name=“可见性”)]
公共布尔可见性{get;set;}
[显示(Name=“可见性主菜单”)]
公共布尔可视性m{get;set;}
[列(“控制器”)]
public class Menu
{
//public Menu()
//{
// {
// this.Templates = new HashSet<MenuTemplate>();
// }
//}
[Key]
public int MenuId { get; set; }
[Column("MenuCaption")]
[Display(Name = "Menu Caption")]
[StringLength(100)]
public string MenuCaption { get; set; }
[Display(Name = "Parent Menu")]
public int? ParentMenuId { get; set; }
public virtual Menu ParentMenu { get; set; }
[Display(Name = "Is Group")]
public bool IsGroup { get; set; }
[Display(Name = "Menu Order")]
public int MenuOrder { get; set; }
[Display(Name = "Visibility")]
public bool Visibility { get; set; }
[Display(Name = "Visibility Main Menu")]
public bool VisibilityMM { get; set; }
[Column("Controller")]
[Display(Name = "Controller")]
[StringLength(100)]
public string Controller { get; set; }
[Column("Action")]
[Display(Name = "Action")]
[StringLength(150)]
public string Action { get; set; }
[Display(Name = "Icon")]
public int? IconID { get; set; }
[ForeignKey("IconID")]
public virtual Icon Icon { get; set; }
public virtual ICollection<MenuTemplate> Templates { get; set; }
}
var dataList = db.Menus.Include(X => X.Icon).ToList();
var ViewModellist = dataList.Join(dataList,
a => a.ParentMenuId,
b => b.MenuId,
(_menu, _parent) => new MenuView
{
MenuId = _menu.MenuId,
Action = _menu.Action,
Controller = _menu.Controller,
IsGroup = _menu.IsGroup,
MenuCaption = _menu.MenuCaption,
MenuOrder = _menu.MenuOrder,
ParentMenuId = _menu.ParentMenuId,
Visibility = _menu.Visibility,
VisibilityMM = _menu.VisibilityMM,
PMenuName = _parent.MenuCaption
}).ToList();
if (PId == 0)
{
var hierarchyList = ViewModellist.Where(x => x.ParentMenuId == null).OrderBy(x => x.MenuOrder).
Select(x => new MenuView
{
MenuId = x.MenuId,
Action = x.Action,
Controller = x.Controller,
IsGroup = x.IsGroup,
MenuCaption = x.MenuCaption,
MenuOrder = x.MenuOrder,
ParentMenuId = x.ParentMenuId,
PMenuName = x.PMenuName,
Visibility = x.Visibility,
VisibilityMM = x.VisibilityMM,
ChildList = GetChildMenulist(x.MenuId, ViewModellist)
}).FirstOrDefault();
return View(hierarchyList);
}
else
{
var hierarchyList = ViewModellist.Where(x => x.MenuId == PId).OrderBy(x => x.MenuOrder).
Select(x => new MenuView
{
MenuId = x.MenuId,
Action = x.Action,
Controller = x.Controller,
IsGroup = x.IsGroup,
MenuCaption = x.MenuCaption,
MenuOrder = x.MenuOrder,
ParentMenuId = x.ParentMenuId,
PMenuName = x.PMenuName,
Visibility = x.Visibility,
VisibilityMM = x.VisibilityMM,
ChildList = GetChildMenulist(x.MenuId, ViewModellist)
}).FirstOrDefault();
return PartialView("_Index", hierarchyList);
}