【问题标题】:Complete Insert/Update/Delete of Child Entities in Entity Framework在实体框架中完成子实体的插入/更新/删除
【发布时间】:2014-12-09 14:33:59
【问题描述】:

我知道以前有人问过这个问题,但是经过长时间的搜索和编码后,我无法找到一种有效且干净的方法。这是我所拥有的:

public class QuestionModel
{
    public int QuestionID { get; set; }
    public string QuestionText { get; set; }

    public IList<QuestionChoiceModel> Choices { get; set; }
}

public class QuestionChoiceModel
{
    public int ChoiceID { get; set; }
    public string ChoiceText { get; set; }
}

我正在为这个 ASP.Net MVC 应用程序使用 EF 5。通用存储库模式和使用 InRequestScope() 的 Ninject 依赖注入已经到位并且工作顺利。这些模型可以毫无问题地映射到实体/从实体映射。

向数据库添加新问题非常简单。我设置了一些 QuestionChoice 实例的 Question 属性,其余部分由 EF 处理。

问题在于更新。假设我们在数据库中有一个带有 3 个 QuestionChoices 的问题:

ChoiceID    QuestionID    ChoiceText
--------    ----------    ----------
1           1             blah blah
2           1             blah blah
3           1             blah blah

当问题的编辑页面打开时(GET:/Questions/Edit/1),我在 Razor 中使用 foreach 显示这 3 个选项。我编写了一些 JQuery 代码,如果用户愿意,可以添加或删除输入元素所需的标记。因此,可能会在客户端编辑 ID=1 的 QuestionChoice,可能会删除 ID=2,并可能会添加新的 ID=4。当用户按下保存按钮(POST:/Questions/Edit/1)时,表单数据完美地绑定回 QuestionModel。模型正确映射到 Question 实体。故事就从这里开始!

现在 Question 实体有一个 QuestionChoices 集合,其中一些已经在数据库中,一些应该添加到数据库中,还有一些应该从数据库中删除。

我读过很多帖子,例如: Entity Framework not saving modified children

我可以用那种肮脏的方式处理编辑。还有新记录:

this._context.Entry(choice).State = EntityState.Added;

但我正在寻找一种更优雅的方式。并且还处理应该删除的记录。在这种情况下使用 EF 处理子实体的完整插入/更新/删除是否有一种好方法?老实说,我对 EF 的期望更高。

【问题讨论】:

    标签: c# asp.net-mvc entity-framework


    【解决方案1】:

    这是一个棘手的问题。不幸的是,我无法提供您喜欢的解决方案。我不相信这是可能的。 EF 无法跟踪对您的实体所做的更改,除非它们是在检索实体的上下文中进行的 - 这在 Web 环境中是不可能的。实现这一点的唯一方法是在 POST 到 /Questions/Edit/1 之后检索 Question 对象(在上下文中),并在 POSTed Question 和从中检索的 Question 之间执行一种“合并”数据库。这将包括在您的QuestionModel 上分配属性,以及使用您发布的QuestionModel 从数据库中检索到的每个QuestionChoiceModel。我会说这也不是一个好习惯,因为你会忘记包含一个属性。它会发生的。

    我能提供的最好(也是最简单)的解决方案是使用上面的.Entry() 方法添加/编辑您的QuestionModelQuestionChoiceModel(s)。您将在此处牺牲“最佳实践”以获得不易出错的解决方案。

    QuestionModel questionFromDb;
    QuestionModel questionFromPost;
    
    QuestionModelChoice[] deletedChoices = questionFromDb.Choices.Where(c => !questionFromPost.Choices.Any(c2 => c2.Id == c.Id));
    
    
    using (var db = new DbContext())
    {
        db.Entry(questionFromPost).State = questionFromPost.Id == 0 ? EntityState.Added : EntityState.Modified;
    
        foreach(var choice in questionFromPost.Choices)
        {
            db.Entry(choice).State = choice.Id == 0 ? EntityState.Added : EntityState.Modified;
        }
    
        foreach(var deletedChoice in deletedChoices)
        {
            db.Entry(deletedChoice).State = EntityState.Deleted;
        }
    
        db.SaveChanges();
    }
    

    【讨论】:

    • 谢谢。我会调查的。
    【解决方案2】:

    这只是概念验证。

    Controler 有 func UpdateModel 但它不适用于包含子记录的更复杂的模型。寻找 TestUpdate

    规则#1:每个表都有 PK Id 列。

    规则#2:必须设置每个 FK。

    规则#3:需要设置级联删除。如果你想删除相关记录。

    规则#4:新记录需要 Id = 0 或更好的为 Null 但 Id 不能为空。

    public class TestController<T> : Controller where T : class
    {
        const string PK = "Id";
    
        protected Models.Entities con;
        protected System.Data.Entity.DbSet<T> model;
        public TestController()
        {
            con = new Models.Entities();
            model = con.Set<T>();
        }
    
        // GET: Default
        public virtual ActionResult Index()
        {
            ViewBag.Result = TempData["Result"];
            TempData["Result"] = null;
    
            var list = model.ToList();
            return View(list);
        }
    
        [HttpGet]
        public virtual ActionResult AddEdit(string id)
        {
            int nId = 0;
            int.TryParse(id, out nId);
    
            var item = model.Find(nId);
            return View(item);
    
        }
    
        [HttpPost]
        public virtual ActionResult AddEdit(T item)
        {
            TestUpdate(item);
    
            con.SaveChanges();
    
            return RedirectToAction("Index");
        }
    
        [HttpGet]
        public virtual ActionResult Remove(string id)
        {
            int nId = 0;
            int.TryParse(id, out nId);
            if (nId != 0)
            {
                var item = model.Find(nId);
                con.Entry(item).State = System.Data.Entity.EntityState.Deleted;
                con.SaveChanges();
            }
            return Redirect(Request.UrlReferrer.ToString());
        }
    
        private void TestUpdate(object item)
        {
            var props = item.GetType().GetProperties();
            foreach (var prop in props)
            {
                object value = prop.GetValue(item);
                if (prop.PropertyType.IsInterface && value != null)
                {
                    foreach (var iItem in (System.Collections.IEnumerable)value)
                    {
                        TestUpdate(iItem);
                    }
                }
            }
    
            int id = (int)item.GetType().GetProperty(PK).GetValue(item);
            if (id == 0)
            {
                con.Entry(item).State = System.Data.Entity.EntityState.Added;
            }
            else
            {
                con.Entry(item).State = System.Data.Entity.EntityState.Modified;
            }
    
        }
    
    }
    

    这里是项目https://github.com/mertuarez/AspMVC_EF/

    您需要为模型创建控制器并为操作创建视图。 如果是 AddEdit 操作,您必须为子类型创建编辑器模板。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-19
      • 2017-02-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-07
      • 2020-12-10
      • 1970-01-01
      相关资源
      最近更新 更多