【华为云技术分享】Entity Framework Core 捕获数据库变动
在实际项目中我们往往需要记录存储在数据库中数据的变动(例如修改数据前记录下数据的原始值),这样一来在发生误操作时可以将数据恢复到变动前的状态,也可以追溯到数据的修改人。大部分开发人员会自己定义记录数据变动的代码,但是这样不仅费时费力有时还会影响到这个业务的性能。当然,我们也可以利用数据库触发器来记录这些操作,在 SQL Server 数据库 2017 以上版本中给我们提供了跟踪数据库数据更改的功能,利用这个功能可以准确的记录数据库数据的变动。这个功能虽然强大但是某些时候我们使用的数据库并不是 SQL Server 数据库,或者某些情况下我们不适合使用 SQL Server 数据库所提供的这个功能。那么这个时候该怎么办呢?如果你使用的是 Entity Framework Core 2.0 及以上版本来开发项目的话,那这个问题就好解决了。在 Entity Framework Core 中,只要捕获到了数据变更记录,我们就可以将数据随时还原到变更前的状态,在这里数据库变更记录被称为审计数据。那么我们先来看两个问题:
审计数据是在什么时候产生并写入数据库的呢?
数据的新旧值是如何获取到的呢?
要解答上述两个问题,那就跟着我一起来看看怎么利用 Entity Framework Core 来捕获审计数据。
零、创建审计模型
捕获审计数据并存入数据库的第一步是创建审计模型,只有具有了审计模型的审计数据才能正确的存入数据库。
public class Audit{ public int Id { get; set; } public string TableName { get; set; } public DateTime DateTime { get; set; }[NotMapped] public Operation Operation { get; set; } public string OperationString{ get { return Operation.ToString(); } private set { Operation = (Operation)Enum.Parse(typeof(Operation), value, true); }} public string Key { get; set; } public string Old { get; set; } /// <summary>/// 操作后的数据/// </summary>public string New { get; set; }
}/// <summary>/// 操作类型/// </summary>public enum Operation
{Add = 0,Delete = 1,Modified = 2}
上述代码创建的审计模型包含***作表的名称 TableName 、操作的类型 Operation 、***作数据的主键 Key 、 操作前的数据 Old 以及操作后的数据 New ,其中操作类型包含了增删改。
一、创建审计数据存储
现在我们有了审计模型,但是只有审计模型还不行,我们还需要创建和存储审计数据相关的类,下面我们就来一起创建这个类。
这个类主要是用于存储表名称,***作数据的主键Id,***作前的数据和***作后的数据。在上面的代码中我们看到我们将***作数据的主键Id、***作前的数据和***作后的数据的变量都定义成了字典类型,这是因为我们的程序中有可能出现批量操作的问题。在将上述信息转换成 Audit 时提示我们对***作前的数据和***作后的数据进行了一个长度判断,这是因为当我们新增数据的时候是没有旧数据的,当我们对数据没有进行任何更改就提交数据的时候是不存在新数据的。
二、重写 SaveChanges
这个例子重写的是 SaveChanges ,对于 SaveChangesAsync 同样适用。我们需要在 OnBeforSaveBehavior 方法中创建 AuditDb 列表。
public class EFContext : DbContext{ public override int SaveChanges(bool acceptAllChangesOnSuccess){List<AuditDb> auditDbs = OnBeforeSaveBehavior(); var result = base.SaveChanges(acceptAllChangesOnSuccess); return result;} List<AuditDb> OnBeforeSaveBehavior(){ChangeTracker.DetectChanges();List<AuditDb> auditDbs = new List<AuditDb>(); foreach (EntityEntry entity in ChangeTracker.Entries()){ if (entity.Entity is Audit || entity.State == EntityState.Detached || entity.State == EntityState.Unchanged){ continue;}AuditDb auditDb = new AuditDb(entity){TableName = entity.Metadata.Name};auditDbs.Add(auditDb); foreach (var property in entity.Properties){ if (property.IsTemporary){auditDb.propertyEntries.Add(property); continue;} var propertName = property.Metadata.Name; if (property.Metadata.IsPrimaryKey()){auditDb.keys[propertName] = property.CurrentValue; continue;} switch (entity.State){ case EntityState.Deleted:auditDb.Operation = Operation.Delete;auditDb.olds[propertName] = property.OriginalValue; break; case EntityState.Modified: if (property.IsModified){auditDb.Operation = Operation.Modified;auditDb.olds[propertName] = property.OriginalValue;auditDb.news[propertName] = property.CurrentValue;} break; case EntityState.Added:auditDb.Operation = Operation.Add;auditDb.news[propertName] = property.CurrentValue; break;}}}List<Audit> audits = new List<Audit>(); foreach (var item in auditDbs.Where(p => !p.HasPropertyEntries)){audits.Add(item.ToAudit());} return auditDbs.Where(p => p.HasPropertyEntries).ToList();}
}
到目前为止,捕获审计数据的所有代码已经完成,这里需要注意的一点是部分实体属性是由数据库生成的,例如当前日期、Id等,这些值需要等待 SaveChanges 方法执行完毕后方可获得,也就是说在这种情况下保存审计数据必须在 SaveChanges 方法之后。
三、总结
通过前面的代码示例和讲解,我们就可以解答前面提出的两个问题了,除了部分数据是由数据库自动生成的情况下,大部分情况下在调用SaveChanges方法之前,我们通过上下文中的ChangeTracker属性来获取旧值和新值并保存。上述代码理解起来比较简单,适用于大部分情况,可以直接放在项目中使用。
作者:华为云享专家 喵叔
【华为云技术分享】Entity Framework Core 捕获数据库变动相关推荐
- 【华为云技术分享】圣诞特别版 | 数据库频频出现OOM问题该如何化解?
本想安安心心过圣诞,结果被频繁出现的OOM问题难倒了?别慌,华为云RDS for MySQL给您支招,不仅给用户提供了合理的默认设置,还提供了一个万能公式,用户可根据公式自行推算和配置数据库合适的总内 ...
- 【华为云技术分享】区块链与数据库如何结合?
[摘要] 区块链和数据库的结合有两种思路: 1) 应用数据库的技术改进区块链的性能.可用性.例如,BigchainDB就是这一方向的典型应用,数据库学术界的研究大多基于这个方向. 2) 利用区块链的不 ...
- 【华为云技术分享】【技术总结】从Hash索引到LSM树
摘要:本文将从实现最简单的Key-Value数据库讲起,然后针对实现过程中遇到的一些瓶颈,采用上述的索引技术,对数据库进行优化,以此达到对数据库的索引技术有一个较为深刻的理解. 前言 数据库算是软件应 ...
- 【华为云技术分享】实战笔记丨JDBC问题定位指南
JDBC(Java数据库连接性)是Java API,用于管理与数据库的连接,发出查询和命令以及处理从数据库获得的结果集.JDBC在1997年作为JDK 1.1的一部分发布,是为Java持久层开发的首批 ...
- 【华为云技术分享】三大前端技术(React,Vue,Angular)探密(下)
[华为云技术分享]三大前端技术(React,Vue,Angular)探密(上) [Angular] Angular(通常被称为 "Angular 2+"或 "Angula ...
- 【华为云技术分享】“技术-经济范式”视角下的开源软件演进剖析-part 1
前言 以互联网为代表的信息技术的迅猛发展对整个经济体系产生了巨大的影响.信息技术的发展一方面使知识的积累和传播更加迅速,知识爆炸性的增长:另一方面,使信息的获取变得越来越容易,信息交流的强度逐渐增加, ...
- 【华为云技术分享】“技术-经济范式”视角下的开源软件演进剖析-part 3
4. 微观层面 4.1 个体动机 在开源软件发展之初, 商业组织的投入很少甚至没有, 完全是靠Richard Stallman 或者 linus Torvalds 这样的个人在努力推动开源软件艰难前行 ...
- oracle精简版_使用Entity Framework Core访问数据库(Oracle篇)
前言 哇..看看时间 真的很久很久没写博客了 将近一年了. 最近一直在忙各种家中事务和公司的新框架 终于抽出时间来更新一波了. 本篇主要讲一下关于Entity Framework Core访问ora ...
- 使用Entity Framework Core访问数据库(DB2篇)
上一篇讲了一些EF Core访问Oracle的坑.(感兴趣请移步:使用Entity Framework Core访问数据库(Oracle篇)) 这篇主要讲一下关于EF Core访问DB2的一揽子~问题 ...
最新文章
- R语言层次聚类:通过内平方和WSS选择最优的聚类K值、可视化不同K下的BSS和WSS、通过Calinski-Harabasz指数(准则)与聚类簇个数的关系获取最优聚类簇的个数
- ABAP-小技巧/知识(1)
- 平安产险_平安产险史良洵:财产险未来三大机遇,新能源车险、非车险以及健康险...
- php读取zip文件,php如何读取zip内容?(zip_entry_read函数的使用)
- 仿京东左侧菜单弹出html代码,相仿京东左侧菜单
- 今天才知道css hack是什么
- 40029错误{“errcode“:40029,“errmsg“:“invalid code, rid: 623bbdcd-3c97f4af-5a2c06d6“}
- Spring Boot文档阅读笔记-对Securing a Web Application解析
- python修改第三方库重写_python安装第三方库时的一些bug 镜像源修改
- 航空三字代码表_航空公司二字及三字代码表
- 领域驱动设计(Domain Driven Design,DDD)
- ftp下载工具绿色版,有没有好用且免费的ftp下载工具绿色版,有几款值得推荐?
- 如何建立一个快速显示桌面的快捷方式?
- HDU 6208 The Dominator of Strings [AC自动机]
- 支付宝面对面支付(境外)
- C语言 CJSON使用实例
- 构建Java体系之感悟系列(一)
- python学习(14)—— 函数、模块和包
- 数据传输的三种交换方式
- HC-SR04 超声波测距模块 IIC使用代码
热门文章
- A*算法(三)算法实现
- java兵乓球队问题_Java两个乒乓球队比赛名单问题(判断素数)
- glide加载图片闪烁_html5 canvas绘制图片
- 【刷题】BZOJ 3653 谈笑风生
- 【转】MySQL双主一致性架构优化
- [Python] Marshmallow QuickStart
- [OS] 远程启动计划任务时以管理员身份运行
- MySQL自定义查询字段排序
- ios多线程之NSThread头文件详解
- LeetCode:Remove Duplicates from Sorted List I II