Complete Insert/Update/Delete of Child Entities in Entity Framework

后端 未结 2 1569
广开言路
广开言路 2020-12-09 21:41

I know it has been asked before, but after long hours of searching and coding I can\'t get to a working and clean approach. Here is what I have:

public class         


        
相关标签:
2条回答
  • 2020-12-09 22:16

    This is a tough problem. Unfortunately, I cannot offer the solution that you prefer. I do not believe it is possible. EF cannot track changes made to your entities unless they are made within the context that the entities are retrieved - which is impossible in a web environment. The only way for this to be possible would be to retrieve the Question object (within a context) after the POST to /Questions/Edit/1, and to perform a type of "merge" between the POSTed Question, and the Question retrieved from the database. This would include assigning properties on your QuestionModel and each QuestionChoiceModel retrieved from the database using your POSTed QuestionModel. I will say that this wouldn't be great practice either, because you WILL forget to include a property. It will happen.

    The best (and easiest) solution I can provide would be to add/edit your QuestionModel and QuestionChoiceModel(s) using the .Entry() method above. You will sacrifice "best practice" here for a solution that will be less error-prone.

    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();
    }
    
    0 讨论(0)
  • 2020-12-09 22:27

    This is just proof of concept.

    Controler have func UpdateModel but it wont work with more complex model which have included child records. Look for TestUpdate.

    Rule#1: Every Table have PK Id column.

    Rule#2: Every FK have to be set.

    Rule#3: Cascade delete need to be set. if you want remove related record.

    Rule#4: New Record need to have Id = 0 or better will be Null but Id cant be 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;
            }
    
        }
    
    }
    

    Here is project https://github.com/mertuarez/AspMVC_EF/

    You need to create Controler for model and create view for action. In case of AddEdit action you have to create editor Template for subtypes.

    0 讨论(0)
提交回复
热议问题