【华为云技术分享】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的一揽子~问题 ...
最新文章
- Mysql 另类盲注中的一些技巧
- cudnn.h: No such file or directory
- Openssl rand命令
- issubclass和isinstance 反射 内置方法(魔术方法)
- PAT甲级1152 Google Recruitment :[C++题解]判质数
- Android资源分离,可分离Android操作系统报告:硬件环境检测文件(十)(分析)...
- java项目嗖嗖移动业务大厅项目报告_晋江市撰写节能评估报告的报告机构立项范本-文瑞...
- CGCTF-Web-md5 collision
- 我遇到的一个怪现象!
- 【面试必备】java面试题视频讲解
- 【算法设计与分析】05 有关函数的渐进的界的定理
- 【分享】费曼技巧---以教促学
- 【Luogu1908】逆序对(离散化,树状数组求逆序对)
- python plt 色卡
- 【bzoj3000】Big Number 【斯特林公式】
- 格式化一个文件的大小(size),或者说是格式化一个app的大小(size)
- c语言计算10亿位圆周率,C语言:圆周率的计算
- rj45接口线序_【自控】全面解析RS232、RS485、RS422、RJ45接口的区别和各自的应用...
- Zabbix离线安装部署
- Golang六款优秀Web框架对比
热门文章
- java群发邮件_161013、java实现邮件群发带附件
- php 如何让html表单当中的数据在修改mysql的时候自动变更_怎么用php把html表单内容写入数据库?...
- macOS Big Sur 11当前存在的一些问题(更新中)
- MapReduce基础
- BZOJ2460: [BeiJing2011]元素(线性基+贪心)
- css连续的纯数字或字母强制换行
- 算法不归路之最大子序列(C++版)
- 剑指offer例题5—逆序输出链表
- 如何在excel 单元格中增加换行
- [转]WebService 之 WSDL文件 讲解