2013-04-07 29 views
0

我有两个模型类 - ParentChild,它们只通过类型化的导航属性进行链接。在没有显式外键id字段的情况下向父节点添加子对象MVC

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; } 
} 

现在我想为使用ASP.Net MVC的父代创建一个新的Child。首先,我需要向用户展示一个视图。我需要以某种方式将父对象键传递给视图。我也想显示ParentName。我只是从数据库中取出Parent对象,创建一个新的Child对象,将其Parent属性设置为提取的parent对象。

public ActionResult Create(int parentId) { 
     var parent = db.Parents.Find(parentId); 
     if (parent == null) { 
      return HttpNotFound(); 
     } 
     var child = new Child() { Parent = parent}; 
     return View(child); 
    } 

用户填写表单后,数据被发送到使用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将其抛出并在数据库中创建了一个新的空父对象(使用不同的键),并将子对象连接到它。在添加childdb.Parents.Attach(child.Parent))之前,我通过将child.Parent附加到数据上下文来解决此问题。

但后来我又遇到了另一个问题。起初,我的模型类是错误的,并没有[Required]属性,从而创建可为空的数据库表列。我添加了该属性,代码停止工作。代码不起作用,因为ModelState.IsValidfalse发生这是因为child.Parent.Name通过childnull

怎样才能解决孩子加入父母的问题?我感兴趣的解决方案:

  • 使用EF代码优先和ASP.Net MVC
  • 不涉及获取从数据库中child.Parent只是为了使模型验证高兴。
  • 不涉及将明确的外键(ParentId)添加到模型。

这可能吗?

+0

你为什么不有'的ParentId”沿W /父。如果你映射它可以让你插入不需要附加/重新加载,并通过索引单独。我一般在说话。这可能会简化你的情况,层次结构总是有点棘手。如果有兴趣,让我知道,如果你不知道如何去做,我会提出并回答。 – NSGaga 2013-04-07 23:20:54

+0

我现在看到那是你的先决条件(我的错误)。但是,这会让你的生活更轻松。此外,建议出于其他原因。 – NSGaga 2013-04-07 23:43:04

回答

1

我认为尝试将父母附加到孩子是有点倒退。通常你会将一个孩子附加到父母身上。最有可能创建新父项是因为您未在子模型中包含具有父项ID的输入元素。所以当Child子类是进入POST的ModelBound时,父ID可能是空的。英孚看到这一点,并认为你也想创建一个新的父母。另外,由于你的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 }); 
    } 
} 
+0

“我认为试图给孩子添加父母是有点倒退的,通常情况下,你会将孩子附加到父母身上。”我将一个孩子附在父母身上。我曾否说过吗? “ – 2013-04-07 04:14:50

+0

”parent id可能为空,EF会看到并认为您也想创建一个新父项。“我在表单中包含了父母标识,并且它可以正确回来。我明确写过“child.Parent'不是null,'child.Parent.Id'是正确的。 EF算法是这样的:它检查'child.Parent'并且发现它不在上下文中;所以,EF添加它(像'db.Parents.Add(child.Parent)'); EF将'Parent'对象发送给db,因为它由db自动生成而抛弃了'Id'; EF用新创建的替换正确的'child.ParentId'并将'child'发送到数据库。 – 2013-04-07 04:21:59

+0

“parentId是你的路线的一部分,你不需要在你的视图模型中指定它”好吧,我没有视图模型,而'Child'shill有父母id信息。 – 2013-04-07 04:26:42

0

Here是一个完整的回答你的问题的链接。简而言之,当您使用断开的实体时,EF不会尊重已设置的实体ID,并会将整个实体图标记为新的(例如已添加)。

我个人不喜欢它,只是overrride调用SaveChanges(),但它的工作原理如下,当你有一个EnityBase基类与一个int(或长期)Id属性(我觉得这非常方便)

public override int SaveChanges() 
{ 
    this.ChangeTracker.Entries() 
     .Where(x => x.Entity is EntityBase && x.State == EntityState.Added && ((EntityBase) x.Entity).Id > 0) 
     .ForEach(x => x.State = EntityState.Unchanged); 
    return base.SaveChanges(); 
} 
+0

正如我写的,我能够通过调用'.Attach'来克服断开的实体问题。主要的问题是,当我将'Required'属性添加到'Parent'属性时,模型验证开始失败,因为只有父项的Id属性被填充。您的链接违反了第三条要求:“不涉及向模型添加显式外键(ParentId)。” – 2013-12-15 03:56:07

+0

对不起,我的坏,我设法回答你的问题的第一部分,没有实际阅读整个事情(如果我会,我会注意到,你设法解决这一部分)。 – MBoros 2013-12-15 12:01:29

+0

至于你真正的问题的答案:child.Parent.Name如何为null?是不是序列化到客户端,然后在您的Create方法中返回值?无论如何,你不能重写DBContext的ValidateEntity方法吗?看起来你想在不添加或修改它的情况下使用父项,因此你可以跳过验证,不是?我会说跳过验证不变的实体可能在这里有意义,不是吗?我没有尝试,但只是一个想法.... – MBoros 2013-12-15 12:05:31

0

认为这应该对创建工作的方法:

public ActionResult Create([Bind(Exclude="Parent")]Child child) 
0
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; } 
} 
+0

欢迎来到堆栈溢出!通常答案应该包括描述,而不仅仅是代码。 – JoelC 2016-04-20 15:10:58

0
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); 
} 
相关问题