# SqlSugar 差异日志功能实现

发布时间:2026/7/30 17:13:35

# SqlSugar 差异日志功能实现 SqlSugar 差异日志功能实现前言在企业级应用开发中数据库操作的审计日志是非常重要的功能。本文介绍如何基于 SqlSugar 框架实现数据库操作的差异日志记录功能能够记录新增、修改、删除操作的具体变更内容。功能特点自动记录在执行数据库操作时自动触发无需手动调用差异对比更新操作记录字段修改前后的值批量收集支持同一请求中多次数据库操作的差异合并统一输出请求结束后合并输出为一条日志核心代码实现1. SqlSugar 配置// 配置 SqlSugar 的 Updateable 对象完成时的回调启用差异日志事件StaticConfig.CompleteUpdateableFuncit{varmethodit.GetType().GetMethod(EnableDiffLogEvent);method.Invoke(it,newobject[]{null});};// 配置 SqlSugar 的 Insertable 对象完成时的回调启用差异日志事件StaticConfig.CompleteInsertableFuncit{varmethodit.GetType().GetMethod(EnableDiffLogEvent);method.Invoke(it,newobject[]{null});};// 配置 SqlSugar 的 Deleteable 对象完成时的回调启用差异日志事件StaticConfig.CompleteDeleteableFuncit{varmethodit.GetType().GetMethod(EnableDiffLogEvent);method.Invoke(it,newobject[]{null});};2. 差异日志处理核心逻辑// 记录差异db.Aop.OnDiffLogEventit{// 操作前记录包含字段描述、列名、值、表名、表描述vareditBeforeDatait.BeforeData;// 操作后记录vareditAfterDatait.AfterData;// 获取操作类型枚举值update、insert、deletevardiffTypeit.DiffType;// 更新操作比较修改前后的差异if(diffTypeDiffType.update){vardatagetDiff(editBeforeData,editAfterData);if(data!null!string.IsNullOrEmpty(data.diffData)){DiffLogCollector.AddDiff(editAfterData[0]?.TableDescription,data.diffData);}}// 插入操作获取新增的字段和值if(diffTypeDiffType.insert){vardatagetInsertDiff(editAfterData);if(data!null!string.IsNullOrEmpty(data.diffData)){DiffLogCollector.AddDiff(editAfterData[0]?.TableDescription,data.diffData);}}// 删除操作获取被删除的字段和值if(diffTypeDiffType.delete){vardatagetDeleteDiff(editBeforeData);if(data!null!string.IsNullOrEmpty(data.diffData)){DiffLogCollector.AddDiff(editBeforeData[0]?.TableDescription,data.diffData);}}};3. 差异比较辅助类publicclassdiffLog{publicstringID{get;set;}publicstringdiffData{get;set;}}/// summary/// 忽略的字段/// /summarypublicstaticreadonlyListstringIgnoreColumnsnewListstring(){id,created_by,created_time,updated_time,createtime,updated_by,IsDeleted,};/// summary/// 比较两个数据对象的修改内容/// /summarypublicstaticdiffLoggetDiff(ListDiffLogTableInfobeforeData,ListDiffLogTableInfoafterData){stringmianIDnull;// 获取主键值if(beforeData!null){varkeyCoulumnbeforeData[0].Columns.FirstOrDefault(pp.IsPrimaryKeytrue);if(keyCoulumn!null){mianIDkeyCoulumn.Value.ToString();}}elseif(afterData!null){varkeyCoulumnafterData[0].Columns.FirstOrDefault(pp.IsPrimaryKeytrue);if(keyCoulumn!null){mianIDkeyCoulumn.Value.ToString();}}StringBuildersbnewStringBuilder();if(beforeData!nullafterData!null){varbefroeColumnsbeforeData[0].Columns;varafterCloumsafterData[0].Columns;foreach(variteminbefroeColumns){if(IgnoreColumns.Contains(item.ColumnName))continue;varafterItemafterCloums.FirstOrDefault(pp.ColumnNameitem.ColumnName!p.Value.Equals(item.Value));if(afterItem!null){sb.Append($[字段:{item.ColumnDescription},修改前:{item.Value}修改后:{afterItem.Value}]);}}}stringdiffDatasb.Length0?$修改:{sb}:;returnnewdiffLog{IDmianID,diffDatadiffData};}/// summary/// 获取插入操作的新增数据内容/// /summarypublicstaticdiffLoggetInsertDiff(ListDiffLogTableInfoafterData){stringmainIDnull;StringBuildersbnewStringBuilder();if(afterData!nullafterData.Count0){varcolumnsafterData[0].Columns;// 获取主键值varkeyColumncolumns.FirstOrDefault(pp.IsPrimaryKeytrue);if(keyColumn!null){mainIDkeyColumn.Value?.ToString();}// 遍历所有字段排除忽略列foreach(varcolincolumns){if(IgnoreColumns.Contains(col.ColumnName))continue;sb.Append($[{col.ColumnDescription}:{col.Value}]);}}stringdiffDatasb.Length0?$新增:{sb}:;returnnewdiffLog{IDmainID,diffDatadiffData};}/// summary/// 获取删除操作的数据内容/// /summarypublicstaticdiffLoggetDeleteDiff(ListDiffLogTableInfobeforeData){stringmainIDnull;StringBuildersbnewStringBuilder();if(beforeData!nullbeforeData.Count0){varcolumnsbeforeData[0].Columns;// 获取主键值varkeyColumncolumns.FirstOrDefault(pp.IsPrimaryKeytrue);if(keyColumn!null){mainIDkeyColumn.Value?.ToString();}// 遍历所有字段排除忽略列foreach(varcolincolumns){sb.Append($[{col.ColumnDescription}:{col.Value}]);}}stringdiffDatasb.Length0?$删除:{sb}:;returnnewdiffLog{IDmainID,diffDatadiffData};}4. 差异日志收集器/// summary/// 差异日志收集器用于在单个 HTTP 请求生命周期内收集多个数据库操作的差异信息/// 最终合并输出一条日志。基于 HttpContext.Items 存储确保每个请求独立不受异步线程切换影响。/// /summarypublicstaticclassDiffLogCollector{privatestaticIHttpContextAccessor_httpContextAccessor;/// summary/// 配置 IHttpContextAccessor 实例必须在应用程序启动时调用/// /summarypublicstaticvoidConfigure(IHttpContextAccessorhttpContextAccessor){_httpContextAccessorhttpContextAccessor;}/// summary/// 获取当前 HTTP 上下文/// /summaryprivatestaticHttpContextCurrent_httpContextAccessor?.HttpContext;/// summary/// 添加一条差异记录到当前请求的收集器中/// /summary/// param nametableName操作的表名/param/// param namediff差异内容字符串/parampublicstaticvoidAddDiff(stringtableName,stringdiff){if(string.IsNullOrEmpty(diff))return;varcontextCurrent;if(contextnull)return;varlistcontext.Items[DiffLogs]asListstring;if(listnull){listnewListstring();context.Items[DiffLogs]list;}list.Add($[{tableName}]{diff});}/// summary/// 获取当前请求收集到的所有差异记录列表/// /summarypublicstaticListstringGetDiffs(){varcontextCurrent;if(contextnull)returnnewListstring();returncontext.Items[DiffLogs]asListstring??newListstring();}/// summary/// 清除当前请求收集的差异记录/// /summarypublicstaticvoidClear(){varcontextCurrent;context?.Items.Remove(DiffLogs);}/// summary/// 指示当前请求是否收集了任何差异记录/// /summarypublicstaticboolHasDiffsGetDiffs().Any();}5. 差异日志中间件/// summary/// 差异日志中间件用于在请求结束后输出合并后的数据库操作差异日志/// /summarypublicclassDiffLogMiddleware{privatereadonlyRequestDelegate_next;privatereadonlyILogger_logger;publicDiffLogMiddleware(RequestDelegatenext,ILoggerDiffLogMiddlewarelogger){_nextnext;_loggerlogger;}publicasyncTaskInvokeAsync(HttpContextcontext){// 清空上次请求残留数据DiffLogCollector.Clear();// 执行真正的请求处理await_next(context);// 请求结束后如果有差异则统一输出一条日志if(DiffLogCollector.HasDiffs){varapiDescriptionGetApiDescription(context);varcombinedDiffDiffLogCollector.GetDiffs();varlogMessage${apiDescription}{combinedDiff};ConsoleHelper.WriteLine($接口描述{apiDescription}{Environment.NewLine});ConsoleHelper.WriteLine($差异数据{Environment.NewLine}{combinedDiff.ToJson()});}}/// summary/// 从当前 HTTP 上下文中获取接口的描述信息/// /summaryprivatestringGetApiDescription(HttpContextcontext){varendpointcontext.GetEndpoint();if(endpoint!null){varmethodInfoendpoint.Metadata.OfTypeControllerActionDescriptor().FirstOrDefault()?.MethodInfo;if(methodInfo!null){vardescAttrmethodInfo.GetCustomAttributeDescriptionAttribute();if(descAttr!null)returndescAttr.Description;}}return;}}6. 中间件注册// 获取 IHttpContextAccessor 实例并配置varhttpContextAccessorapp.Services.GetRequiredServiceIHttpContextAccessor();DiffLogCollector.Configure(httpContextAccessor);// 注册差异日志中间件app.UseMiddlewareDiffLogMiddleware();日志输出示例接口描述批量分发物料接口 差异数据 [[物料分配表]新增: [使用组织ID:2044354624294621184][物料库ID:2052592996469313536][物料主图:xxx.webp][物料编码:trer][物料名称:te]..., [物料分配图片表]新增: [物料分配ID:2052664953516724224][图片URL:xxx.webp], [物料分配表]新增: [使用组织ID:2044354028762173440]..., [物料分配图片表]新增: [物料分配ID:2052664953554472960][图片URL:xxx.webp]]使用前提实体类字段需添加 Description 特性用于显示字段中文描述注册 IHttpContextAccessor在Program.cs中添加services.AddHttpContextAccessor()特性引用使用[Description(xxx)]特性标注控制器方法[HttpPost(BatchDistribute)][Description(批量分发物料接口)]publicasyncTaskResultBatchDistribute(...){// 业务代码}总结通过 SqlSugar 的OnDiffLogEvent事件和HttpContext.Items我们实现了一个轻量级的数据库审计日志功能。该方案无侵入不需要在每个业务方法中手动记录日志高性能只在数据库操作时记录批量合并输出易使用配合特性可以自动获取接口描述可扩展可以很方便地对接不同的日志存储系统希望这个方案对你有所帮助如有问题欢迎留言讨论。

相关新闻