先上一段最近项目中的代码,此代码可以放到自己项目中的dbContext中

  1. public override Task<int> SaveChangesAsync()
  2. {
  3. List<AuditLog> AuditLogs = new List<AuditLog>();
  4. List<DataLensTrackingLog> trackinglogs = new List<DataLensTrackingLog>();
  5. var changeTracker = ChangeTracker.Entries().Where(p => p.State == EntityState.Added || p.State == EntityState.Deleted || p.State == EntityState.Modified);
  6. try
  7. {
  8. foreach (var entity in changeTracker)
  9. {
  10. AuditLogs.Clear();
  11. XmlDocument doc = new XmlDocument();
  12. doc.AppendChild(doc.CreateElement(TrackingLog.Records));
  13. if (entity.Entity != null)
  14. {
  15. var entityName = ObjectContext.GetObjectType(entity.Entity.GetType()).Name;
  16. //string entityName = entity.Entity.GetType().Name;
  17. EntityState state = entity.State;
  18. switch (entity.State)
  19. {
  20. case EntityState.Modified:
  21. //entityName = ObjectContext.GetObjectType(entity.Entity.GetType()).Name;
  22. foreach (string prop in entity.OriginalValues.PropertyNames)
  23. {
  24. object currentValue = entity.CurrentValues[prop];
  25. object originalValue = entity.GetDatabaseValues()[prop];//OriginalValues[prop];
  26. if (!Object.Equals(currentValue, originalValue)&&entity.Property(prop).IsModified==true
  27. &&prop.ToLower()!="lastupdateby")
  28. {
  29. AuditLogs.Add(new AuditLog
  30. {
  31. EntityName = entityName,
  32. RecordID = PrimaryKeyValue(entity),
  33. State = state,
  34. ColumnName = prop,
  35. OriginalValue = Convert.ToString(originalValue),
  36. NewValue = Convert.ToString(currentValue),
  37. });
  38. }
  39. }
  40. if (AuditLogs.Count > )
  41. {
  42. TrackingLog.GetXmlForUpdate(doc, AuditLogs);
  43. trackinglogs.Add(new DataLensTrackingLog
  44. {
  45. EntityName = entityName,
  46. Email = this.Email,
  47. XmlDoc = CompressionHelper.Compresse("XmlDoc", new UTF8Encoding().GetBytes(doc.OuterXml)),
  48. CreateTime = DateTime.Now
  49. });
  50. }
  51. break;
  52. case EntityState.Added:
  53. //entityName = ObjectContext.GetObjectType(entity.Entity.GetType()).Name;
  54. foreach (string prop in entity.CurrentValues.PropertyNames)
  55. {
  56. AuditLogs.Add(new AuditLog
  57. {
  58. EntityName = entityName,
  59. RecordID = PrimaryKeyValue(entity),
  60. State = state,
  61. ColumnName = prop,
  62. OriginalValue = string.Empty,
  63. NewValue = entity.CurrentValues[prop],
  64. });
  65.  
  66. }
  67. TrackingLog.GetXmlForUpdate(doc, AuditLogs);
  68. trackinglogs.Add(new DataLensTrackingLog
  69. {
  70. EntityName = entityName,
  71. Email = this.Email,
  72. XmlDoc = CompressionHelper.Compresse("XmlDoc", new UTF8Encoding().GetBytes(doc.OuterXml)),
  73. CreateTime = DateTime.Now
  74. });
  75. break;
  76. case EntityState.Deleted:
  77. //entityName = ObjectContext.GetObjectType(entity.Entity.GetType()).Name;
  78. foreach (string prop in entity.OriginalValues.PropertyNames)
  79. {
  80. AuditLogs.Add(new AuditLog
  81. {
  82. EntityName = entityName,
  83. RecordID = PrimaryKeyValue(entity),
  84. State = state,
  85. ColumnName = prop,
  86. OriginalValue = entity.OriginalValues[prop],
  87. NewValue = string.Empty,
  88. });
  89.  
  90. }
  91. TrackingLog.GetXmlForUpdate(doc, AuditLogs);
  92. trackinglogs.Add(new DataLensTrackingLog
  93. {
  94. EntityName = entityName,
  95. Email = this.Email,
  96. XmlDoc = CompressionHelper.Compresse("XmlDoc", new UTF8Encoding().GetBytes(doc.OuterXml)),
  97. CreateTime = DateTime.Now
  98. });
  99. break;
  100. default:
  101. break;
  102. }
  103. }
  104. }
  105. DataTable dt=TypeConvert.ToDataTable(trackinglogs);
  106. SqlDataHelper.SqlBulkCopy(dt, "DataLensTrackingLog", DataBaseType.ConnLogDataStr);
  107. return base.SaveChangesAsync();
  108. }
  109. catch (Exception ex)
  110. {
  111. throw ex;
  112. }
  113. }

EF6中可以覆写SaveChangesAsync(异步)或者SaveChanges来实现记录变化的跟踪,这其中包括新增、修改、和删除,
dbContext中的属性ChangeTracker可以跟踪属性的变化,即查找实体修改记录:ChangeTracker.Entries().Where(p => p.State ==EntityState.Modified);
查找新增的实体记录行:ChangeTracker.Entries().Where(p => p.State == EntityState.Added);查找删除的实体记录行:
ChangeTracker.Entries().Where(p => p.State == EntityState.Deleted)

获取实体的名字是ObjectContext.GetObjectType(entity.Entity.GetType()).Name,之前获取实体是通过以下方式:
entity.Entity.GetType().Name,但是这种方式会有一个问题,假如说存在以下实体

  1. public partial class DataLensDataPointUniverse
  2. {
  3. [Key]
  4. public int DataPointUniverseId { get; set; }
  5. public int DomicileId { get; set; }
  6. public int ShareClassUniverseId { get; set; }
  7. public int DataPointId { get; set; }
  8. public int CollectionStatusId { get; set; }
  9. public Nullable<System.DateTime> CollectionStartDate { get; set; }
  10. public Nullable<System.DateTime> CollectionEndDate { get; set; }
  11. public int CollectionFrequencyId { get; set; }
  12. public int CollectionPriorityId { get; set; }
  13. public string Restrictions { get; set; }
  14. public int FundFormId { get; set; }
  15. public string InterpretationRules { get; set; }
  16. public string Comments { get; set; }
  17. public string QualityMeasureCompleteness { get; set; }
  18. public string QualityMeasureTimeliness { get; set; }
  19. public string QualityMeasureAccuracy { get; set; }
  20. public int FundCollectionRequirementId { get; set; }
  21. public int LEANProjectId { get; set; }
  22. public Nullable<System.Guid> CreateBy { get; set; }
  23. public Nullable<System.Guid> LastUpdateBy { get; set; }
  24. public Nullable<System.DateTime> CreateDate { get; set; }
  25. public Nullable<System.DateTime> LastUpdateDate { get; set; }
  26.  
  27. public virtual DataLensDataPoint DataLensDataPoint { get; set; }
  28. }

这时候获取的实体名字就可能是类似于这种格式343434343_DataLensDataPoint_3434,为什么会存在这种格式呢,因为该实体中的DataPointId是另个属性 public virtual DataLensDataPoint DataLensDataPoint { get; set; }中的外键,即(即实体DataLensDataPoint中DataPointId)


    
 获取属性当前值entity.CurrentValues[prop](即还未保存到数据库),获取数据库当前值entity.GetDatabaseValues()[prop]
其中的entity.Property(prop).IsModified==true属性是来判断确实该值要存到数据库中,才要去记录日志,因为在Save
之前可能有些实体属性被标记为不做更改entity.Property(prop).IsModified==false;

获取实体主键的值可以用以下方法

  1. private string PrimaryKeyValue(DbEntityEntry entry)
  2. {
  3. var objectStateEntry = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity);
  4. if (null == objectStateEntry.EntityKey.EntityKeyValues)
  5. {
  6. return string.Empty;
  7. }
  8. return string.Join(",", objectStateEntry.EntityKey.EntityKeyValues.Select(item => item.Value.ToString()).ToArray());
  9. }

当我们进行数据批量修改的时候我们可以用到EntityFramework.Extended,而不至于一条条的去更新数据库,增添了与数据库的I/O次数,具体的github地址地址为:

https://github.com/loresoft/EntityFramework.Extended,但是同时会有个问题,我们就没法做entitiframework的tracking log的使用,即使用SaveChanges的时候捕捉不到任何变化,如果想使用批量,又能捕捉到变化,可以使用entitiframework来执行sql语句来批量更新,并且也能捕获到数据改变的情况,因为使用了SaveChanges(),参考链接为:/wujingtao/p/5412329.html

Note:EntityFramework.Extended也支持事务.

原文:http://www.jianzhaoyang.com/cby-love/p/5746654.html

EF利用重写SaveChanges()方法实现 审计日志记录的更多相关文章

  1. EF里查看/修改实体的当前值、原始值和数据库值以及重写SaveChanges方法记录实体状态

    本文目录 查看实体当前.原始和数据库值:DbEntityEntry 查看实体的某个属性值:GetValue<TValue>方法 拷贝DbPropertyValues到实体:ToObject ...

  2. 【数据库】通过触发器实现审计日志记录-demo篇

      触发器实现审计日志记录(记录增.删.改) #创建测试表 CREATE TABLE COMPANY( ID INT PRIMARY KEY NOT NULL, NAME TEXT NOT NULL, ...

  3. 利用paramiko的demo_simple.py进行日志记录时遇到的特殊字符

    特殊字符列表: 回车 "\r" "\x13" 响铃 "\x07" 换行 "\n" "\x10" &q ...

  4. ABP理论学习之审计日志

    返回总目录 本篇目录 介绍 配置 通过特性开启/关闭 注意 我项目中的例子 介绍 维基百科说: "审计跟踪(也叫审计日志)是与安全相关的按照时间顺序的记录,记录集或者记录源,它们提供了活动序 ...

  5. ABP官方文档翻译 4.6 审计日志

    审计日志 介绍 关于IAuditingStore 配置 通过特性启用/禁用 注意事项 介绍 维基百科:“审计追踪(也称为审计日志)是与安全相关的按时间先后的记录.记录集合.记录的目的地和源,提供一系列 ...

  6. Kubernetes审计日志方案

    前言 当前Kubernetes(K8S)已经成为事实上的容器编排标准,大家关注的重点也不再是最新发布的功能.稳定性提升等,正如Kubernetes项目创始人和维护者谈到,Kubernetes已经不再是 ...

  7. Abp + MongoDb 改造默认的审计日志存储位置

    一.背景 在实际项目的开发当中,使用 Abp Zero 自带的审计日志功能写入效率比较低.其次审计日志数据量中后期十分庞大,不适合与业务数据存放在一起.所以我们可以重新实现 Abp 的 IAuditi ...

  8. 最全Kubernetes审计日志方案

    前言 当前Kubernetes(K8S)已经成为事实上的容器编排标准,大家关注的重点也不再是最新发布的功能.稳定性提升等,正如Kubernetes项目创始人和维护者谈到,Kubernetes已经不再是 ...

  9. 基于.NetCore3.1系列 —— 日志记录之日志配置揭秘

    一.前言 在项目的开发维护阶段,有时候我们关注的问题不仅仅在于功能的实现,甚至需要关注系统发布上线后遇到的问题能否及时的查找并解决.所以我们需要有一个好的解决方案来及时的定位错误的根源并做出正确及时的 ...

随机推荐

  1. 趣味编程:静夜思(Kotlin版)

    import java.util.* fun verticalWriting(txt:String, offset:Int) = txt.mapIndexed { i, c -> Pair(i, ...

  2. Python下HttpHTTPClient和AsyncHTTPClient

    HTTPClient 使用例子: from tornado.httpclient import HTTPClient def synchronous_fetch(url): http_client = ...

  3. 如何配置windows定时任务

    Windows上配置任务定时执行有两种方法.一是通过控制面板中的界面配置,另外一种是通过schtasks命令配置.如果是简单的定时任务配置(比如每天单次执行)建议选择界面的方式,简洁.直观.易上手.如 ...

  4. 学JS的心路历程-函式(六)其余参数及预设参数

    今天我们要来介绍ES6新增的其余参数及预设参数! 其余参数rest parameter …numbers可以让我们表示不确定数量的参数,并将其视为一个数组: function getVal(…numb ...

  5. hive 安装centos7

    wget mirror.bit.edu.cn/apache/hive/hive-2.3.4/apache-hive-2.3.4-bin.tar.gz 解压到/usr/local/apache-hive ...

  6. 头部尾部始终处于两端(适用于pc端和移动端)

    此代码展示的效果阐述:(随着屏幕宽高度的变化而变化) 当页面内容小于屏幕高度时,头尾分别处在屏幕顶部和屏幕底部: 当页面出现滚动条时,头尾分别处于屏幕顶部和内容底部: <style> *{ ...

  7. java+selenium自动化实践

    git+java+selenium+testng +maven+idea 1.git之代码维护(下载.分支切换.上传) 下载命令 "git clone git@github.com:Luna ...

  8. 03_java基础(五)之项目结构搭建

    1.结构图 2.dao代码编辑 package com.day01.station.dao; /** * Created by Administrator on 2018/2/1. */ public ...

  9. sql 求max和min,但是第二大,第二小怎么算?

    利用子查询,但这样速度较慢. SELECT Baoming.id, Baoming.addtime AS '报名时间', CONCAT(Members.realname,Members.usernam ...

  10. display:none vs visibility:hidden

    [display:none vs visibility:hidden] 设置元素的display为none是最常用的隐藏元素的方法. 1 .hide { 2 display:none; 3 } 将元素 ...