1

我有两个模型类——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; }
}

现在我想Child使用 ASP.Net MVC 为父级创建一个新的。首先,我需要向用户展示一个视图。我需要以某种方式将父对象键传递给视图。我还想显示 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);
    }

Create用户填写表单后,使用 HTTP POST将数据发送到操作。

    [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不是 null 并且是正确的child.Parent.Id,但是 EF 将其丢弃并在数据库中创建了一个新的空父(使用不同的密钥)并将子链接到它。我已通过在添加( )child.Parent之前将 附加到数据上下文来解决此问题。childdb.Parents.Attach(child.Parent)

但后来我遇到了另一个问题。起初,我的模型类是错误的并且没有[Required]属性,因此创建了可为空的数据库表列。我添加了属性,代码停止工作。代码不起作用,因为ModelState.IsValidisfalse发生是因为child.Parent.Name传递了childis null

如何解决将孩子添加到父母的问题?我对以下解决方案感兴趣:

  • 使用 EF Code-First 和 ASP.Net MVC
  • 不涉及child.Parent从数据库中获取只是为了让模型验证器满意。
  • 不涉及向ParentId模型添加显式外键 ( )。

这可能吗?

4

5 回答 5

1

我认为试图将父母与孩子联系起来有点倒退。通常,您会将孩子附加到父母身上。很可能正在创建新的父级,因为您没有在子模型中包含具有父级 ID 的输入元素。所以当 Child child 是 ModelBound 进入 POST 时, parent 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 });
    }
} 
于 2013-04-07T02:56:52.253 回答
0

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

我个人不喜欢它,只是重写 SaveChanges(),尽管当你有一个带有 int(或 long)Id 属性的 EnityBase 基类时它的工作原理如下(我觉得这非常方便)

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();
}
于 2013-12-13T15:57:03.483 回答
0

认为这应该适用于 Create 方法:

public ActionResult Create([Bind(Exclude="Parent")]Child child)
于 2014-10-14T13:36:16.680 回答
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);
}
于 2016-04-20T14:50:40.623 回答
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; }
}
于 2016-04-20T14:46:10.007 回答