我知道之前有人问过,但经过长时间的搜索和编码后,我无法采用干净利落的方法。这就是我所拥有的:
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。 Ninject使用InRequestScope()
的通用存储库模式和依赖注入已经到位并且运行顺利。这些模型在没有问题的情况下映射到实体/从实体映射。
向数据库添加新问题很简单。我设置了一些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。当用户按下Save按钮(POST:/ Questions / Edit / 1)时,表单数据将完美地绑定到QuestionModel。模型正确映射到Question实体。这就是故事开始的地方!
现在,Question实体有一个QuestionChoices集合,其中一些已经在数据库中,一些应该添加到数据库中,一些应该从数据库中删除。
我读过很多帖子,比如: Entity Framework not saving modified children
我可以用那种肮脏的方式处理编辑。还有新记录:
this._context.Entry(choice).State = EntityState.Added;
但我正在寻找一种更优雅的方式。并处理应删除的记录。在这种情况下使用EF处理子实体的完整插入/更新/删除是否有一种好的方法?老实说,我期待更多来自EF。
答案 0 :(得分:7)
这是一个棘手的问题。不幸的是,我无法提供您喜欢的解决方案。我不相信这是可能的。 EF无法跟踪对您的实体所做的更改,除非它们是在检索实体的上下文中进行的 - 这在Web环境中是不可能的。唯一可行的方法是在POST到/ Questions / Edit / 1之后检索Question对象(在上下文中),并在POSTed Question和从中检索的问题之间执行一种“合并”。数据库。这包括使用您的POST QuestionModel
为您的QuestionChoiceModel
和从数据库中检索到的每个QuestionModel
分配属性。我会说这也不是很好的做法,因为你会忘记包含一个属性。它会发生。
我可以提供的最佳(也是最简单)解决方案是使用上面的QuestionModel
方法添加/修改您的QuestionChoiceModel(s)
和.Entry()
。您将牺牲“最佳实践”来获得一个不易出错的解决方案。
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();
}
答案 1 :(得分:0)
这只是概念证明。
控制器有func UpdateModel ,但它不适用于包含子记录的更复杂的模型。寻找 TestUpdate 。
规则#1:每个表都有PK Id列。
规则#2:必须设置每个FK。
规则#3:需要设置级联删除。如果你想删除相关记录。
规则#4:新记录需要Id = 0或更高才能为空但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/
您需要为模型创建Controler并创建视图以进行操作。 如果是AddEdit操作,则必须为子类型创建编辑器模板。