在实体框架中完成子实体的插入/更新/删除

本文关键字:实体 插入 删除 更新 框架 | 更新日期: 2023-09-27 18:26:26

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

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

我将EF 5用于这个ASP.Net MVC应用程序。Generic Repository Pattern和使用InRequestScope()的Ninject依赖注入已经到位,并且工作顺利。这些模型可以毫无问题地映射到实体/从实体映射到实体。

将新问题添加到数据库是直接的。我设置了一些QuestionChoice实例的Question属性,EF处理其余实例。

问题在于更新。假设我们在数据库中有一个问题,有3个问题选择:

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。当用户按下Save按钮(POST:/Questions/Edit/1)时,表单数据会完美地绑定回QuestionModel。模型已正确映射到Question实体。这就是故事的开始!

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

我读过很多帖子,比如:实体框架未保存修改的子

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

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

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

在实体框架中完成子实体的插入/更新/删除

这是一个棘手的问题。不幸的是,我无法提供您喜欢的解决方案。我认为这是不可能的。EF无法跟踪对实体所做的更改,除非这些更改是在检索实体的上下文中进行的——这在web环境中是不可能的。唯一可行的方法是在POST到/Questions/Edit/1之后检索Question对象(在上下文中),并在POST的Question和从数据库检索的Questions之间执行一种类型的"合并"。这将包括在QuestionModel和使用POSTed 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();
}

这只是概念的证明

Controler具有函数UpdateModel,但它无法处理包含子记录的更复杂的模型。查找TestUpdate

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

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

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

规则#4:新记录的Id必须为0或更高。将为Null,但Id不能为Null。

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/

您需要为模型创建Controler,并为动作创建视图。在AddEdit操作的情况下,您必须为子类型创建编辑器模板。