和大家脱离了一段时间,有时候总想着时间挤挤总是会有的,但是并非人愿,后面会借助周末的时间来打理博客,如有问题可以在周末私信我或者加我QQ皆可,欢迎和大家一起探讨,本节我们来讨论EF Core中的一些问题后面陆陆续续会将EF Core中需要注意的地方补充上来,有些是我一直以来比较疏忽的地方,不喜勿喷。用在实际项目中的时候才发现和平时所学有很大差异,靠着项目才能检验出真理。
EntityFramework Core问题集锦 更新单个实体更新单个实体的方式有两种:
(1)查询出实体进行赋值更新
说的更专业一点则是已被跟踪的实体进行赋值更新,此时实体已被快照,此时进行更新时只需要调用SaveChanges或者SaveChangesAsync,当已赋值属性与快照中值不同时,此时调用SaveChangesAsync或者SaveChanges方法时会将此属性的状态即(IsModified)修改为True,否则为False。代码大概如下:
public async Task<bool> UpdateStatus(int id, byte status) { var blog = _efCoreContext.Blogs.Find(id); blog.Status = status; var effectRows = await _efCoreContext.SaveChangesAsync(CancellationToken.None); if (effectRows > 0) { return true; } return false; }
但是如上又带来一个问题,我们通过影响行数来获取是否更新成功,如果想更新某一列,但是此列的值未进行改变,此时与快照中的值一致,则受影响行数为0,结果返回的更新失败,如下所示:
在这种情况就需要一个扩展方法来显式指定更新属性即使值未发生改变也将其属性状态IsModified修改为True,这样才不会导致值未改变但是更新失败的情况,即如下:
_efCoreContext.Entry(blog).Property(d => d.Status).IsModified = true;
(2)未查询出实体进行赋值更新。
当此实体未进行查询,此时需要调用Update来将此实体状态中所有属性的IsModified修改为True,此时代码大概如下:
public async Task<bool> UpdateStatus(Blog blog) { _efCoreContext.Blogs.Update(blog); var effectRows = await _efCoreContext.SaveChangesAsync(CancellationToken.None); if (effectRows > 0) { return true; } return false; }
也就是说如果是明确实体所有属性都会更改则可以利用Update方法来更新所有属性,否则不需要更新的属性比如常见场景:数据库中表中数据创建时间下次进行更新时是不需要更新,如若调用Update方法,如果对创建时间赋值会进行覆盖,未赋值则会显示DateTime默认时间。
批量更新之表达式树批量更新的场景大有,在我们项目中选择多个产品将产品的状态更新为下架状态,下面我们来还原场景。创建批量更新接口,此时数据库中数据如下:
Task<bool> UpdateStatus(int[] ids);
我们将Blog中状态中为0的行更新为1,此时接口则如下:
public async Task<bool> UpdateStatus(int[] ids) { var blogs = _efCoreContext.Blogs.Where(d => ids.Contains(d.Id)); blogs.Select(b => new Blog() { Id = b.Id, Status = 1 }).ToList(); if (await _efCoreContext.SaveChangesAsync(CancellationToken.None) > 0) { return true; } return false; }
此时更新肯定不能正确更新,其原因不必多讲,由于是更新集合中的指定属性,此时我写了关于单个和集合更新指定属性的扩展方法,如下:
EfCoreUpdateExe { Update<T>(this EFCoreContext context, T entity, params Expression<Func<T, object>>[] properties) where T : class, new() { var dbEntityEntry = context.Entry(entity); if (properties.Any()) { foreach (var property in properties) { dbEntityEntry.Property(property).IsModified = true; } } else { foreach (var rawProperty in dbEntityEntry.Entity.GetType().GetTypeInfo().DeclaredProperties) { var originalValue = dbEntityEntry.Property(rawProperty.Name).OriginalValue; var currentValue = dbEntityEntry.Property(rawProperty.Name).CurrentValue; foreach (var property in properties) { if (originalValue != null && !originalValue.Equals(currentValue)) dbEntityEntry.Property(property).IsModified = true; } } } } UpdateRange<TEntity>(this EFCoreContext context, IEnumerable<TEntity> entities, bool isNoTracking = true, params Expression<Func<TEntity, object>>[] properties) where TEntity : class, new() { foreach (var entity in entities) { var dbEntityEntry = context.Entry(entity); (!isNoTracking) { dbEntityEntry.State = EntityState.Unchanged; } if (properties.Any()) { foreach (var property in properties) { dbEntityEntry.Property(property).IsModified = true; } } else { foreach (var rawProperty in dbEntityEntry.Entity.GetType().GetTypeInfo().DeclaredProperties) { var originalValue = dbEntityEntry.Property(rawProperty.Name).OriginalValue; var currentValue = dbEntityEntry.Property(rawProperty.Name).CurrentValue; foreach (var property in properties) { if (originalValue != null && !originalValue.Equals(currentValue)) dbEntityEntry.Property(property).IsModified = true; } } } } } }
然后代码更新代码修改如下: