-
Notifications
You must be signed in to change notification settings - Fork 0
[Netcore] 使用EF更新资料时仅更新变更的字段
Jinxin Chen edited this page Dec 11, 2019
·
1 revision
如果我们建立了Model,然后通过vs code generation出CRUD的代码,Edit的代码类似下面这样:
public async Task<IActionResult> Edit(string id, [FromForm] Article article)
{
if (id != article.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
_dbContext.Update(item);
await _dbContext.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(article);
}
如果查看log,会发现生成的update sql更新了所有字段,即使我们只更新了某一个字段:
UPDATE `Article` SET `Body` = @p2, `CategoryId` = @p4, `Clicks` = @p6, `DatePublished` = @p8, `Image` = @p10, `IsPublished` = @p12, `
Slug` = @p14, `Summary` = @p16, `Tags` = @p18, `Title` = @p20
WHERE `Id` = @p0;
将代码调整一下,从context中取得既有的entity,赋值之后再更新:
var existItem = await _dbContext.Article.FirstAsync(r => r.Id == id);
existItem.Title = article.Title;
existItem.Summary = article.Summary;
existItem.Body = article.Body;
existItem.Slug = article.Slug;
existItem.IsPublished = article.IsPublished;
existItem.DatePublished = article.DatePublished;
existItem.CategoryId = article.CategoryId;
await _dbContext.SaveChangesAsync();
仅更新Summary字段,查看log,发现生成的sql仅更新Summary字段:
UPDATE `Article` SET `Summary` = @p2
WHERE `Id` = @p0;
查看entity framework的源码可以看到,ChangeDetector已经做好了字段的比对,只有和原值不相等的属性才会被标记为修改状态,才会被更新:
public virtual void DetectChanges(InternalEntityEntry entry)
{
var entityType = entry.EntityType;
foreach (var property in entityType.GetProperties())
{
if (property.GetOriginalValueIndex() >= 0
&& !Equals(entry[property], entry.GetOriginalValue(property)))
{
entry.SetPropertyModified(property);
}
}
foreach (var property in entityType.GetProperties())
{
DetectKeyChange(entry, property);
}
if (entry.HasRelationshipSnapshot)
{
foreach (var navigation in entityType.GetNavigations())
{
DetectNavigationChange(entry, navigation);
}
}
}
文件在这里:
https://github.com/aspnet/EntityFramework/blob/ab9c919d2496ae7e655331328483b634dc3a4f7b/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs#L121
上面的方法,要对entity的每个属性都进行赋值,操作比较繁琐,也不是一个优秀码农想要的。
这里可以使用 AutoMapper 包来简化操作,它可以帮助我们将一个对象映射到另一个对象,简化后的代码如下(Initialize方法可以放入statup.cs中,执行一次就好):
AutoMapper.Mapper.Initialize(cfg => cfg.CreateMap<Article, Article>());
AutoMapper.Mapper.Map(item, existItem);
AutoMapper 的 github 地址如下:
https://github.com/AutoMapper/AutoMapper