我正在编写一个.net / entity框架代码段,该代码段应使用UI传递的最新数据来更新/删除一堆MS SQL行。 假设该表格最初有20行,而最新的集合包含15条记录。在15个变化中,有9个变化,而6个保持不变。因此,将更新9行,并从表中删除不在最新集合中的5行。 所以我的问题是,执行此操作的最佳方法是什么:如果我遍历所有20行并尝试查找每行,则该行将为O(mn)。删除所有表行并重新插入它们可能会更快,但我不确定。 感谢所有帮助!
答案 0 :(得分:1)
因此,您有一个用户界面元素,其中填充了某些类的项目。您还拥有一个数据库,该数据库的表中填充了相同类的项目。
IEnumerable<MyClass> userInterfaceElements = ...
IQueryable<MyClass> databaseElements = ...
注意:查询尚未执行!
您想要更新数据库,以便在更新后数据库包含用户界面元素中的项目。
您没有写出如何确定用户界面元素是否也在数据库中的方法。 假设您没有发明主键。这意味着您的主键具有默认值(零)的元素是不在数据库中的元素。
var itemsToAdd = userInterfaceElements.Where(row => row.Id == 0);
var itemsToUpdate = userInterfaceElements.Where(row => row.Id != 0);
var idsItemsToKeep = itemsToUpdate.Select(row => row.Id);
var itemsToRemove = databaseElements.Where(row => !idsItemsToKeep.Contains(row.Id))
最后一个:删除ID不在您的用户界面元素中的所有项目。
注意:我们仍然没有执行任何查询!
将项目添加到数据库将更改databaseElements,因此在进行任何更改之前,需要具体化这些项目
var addList = itemsToAdd.ToList();
var updateList = itemsToUpdate.ToList();
var removeList = itemsToRemove.ToList();
现在,您已经查询数据库一次:您已提取所有要删除的项目。您不能命令实体框架删除项而不先获取它们。
dbContext.MyClasses.RemoveRange(removeList);
dbContext.MyClasses.AddRange(addList);
要在实体框架中进行更新,正确的方法是获取数据,然后更改属性。
有些人喜欢将项目附加到dbContext的更改跟踪器,并告知它已更改。但是,如果其他人更改了这些项目的某些属性,尤其是如果您未在用户界面元素中显示这些值,则这可能很危险。因此,仅当您确实有很长的项目要更新时,才执行此操作。
正确的方法:
foreach(var itemToUpdate in updateList)
{
var fetchedItem = dbContext.MyClasses.Find(itemToUpdate.Id);
// TODO: update changed properties of the fetchedItem with values from itemToUpdate
}
危险方法:
foreach(var itemToUpdate in updateList)
{
dbContext.Entry(itemToUpdate).State = entityState.Modified;
}
最后:
dbContext.SaveChanges();
用数据库值填充用户界面元素时,您遇到了问题,并且其他一些过程从数据库中删除了其中一个值。
当您的代码查看主键时,它将认为它在数据库中,但是现在不存在了。该元素怎么办?再次添加?好像用户也希望删除它一样?
为解决这类问题,人们通常不会从数据库中删除项目,而是声明它们已过时。他们在表中添加了一个布尔列,指示是否在不久的将来要删除该项目。这解决了人们想要更新项目而其他人希望将其删除的问题。
通常,每个月左右都会启动一个过程,以删除所有过时的对象。您想要更新过时的对象的机会要低得多。
如果需要完全保存,请执行以下操作:不记得布尔型已过时,而是已过时的日期。定期删除所有较长时间过时的项目。
关于过时的一件好事是,如果有人意外宣布某件物品过时,那么仍然有一些时间来修复它。