有一些非常重要的数据表,里面的信息非常重要,我们希望可以追踪每个字段每个单元格的新增、修改、删除记录,那么我们应该如何做到这样的效果呢?
第一步,我们需要设计一个保存记录修改日志的表格。经过思考,我觉得我们至少需要一些下列的字段,如果你有其他需求也可以自己自定义。
表名称 | 主键字段名 | 修改时间 | 修改者 | 修改内容 | 修改主键值 | 修改类型 |
入库单明细 | 入库单 | 2023-3-21 13:23 | 员工A | 列【数量】从【12.2】修改成了【13】,列【单价】从【1.2】修改成了【1.3】 | RK202303210002 | 修改、新增、删除 |
第二步,我们需要在表格的BeforeSave事件中写代码,以记录下相应的修改信息到我们上面设计的数据库表中去。这里我们利用到了SmGrid里面的GetChangesInfo方法,此方法会返回一个ChangesInfo对象,里面包含三个字典属性,字典包含了相应状态下对应行的修改信息。
Vb.Net |
'为了演示,取当前表 Dim tbl As SmGrid=Proj.CurrentSmGrid '日志要保存的数据源 Dim db As Database=Proj.SysDataFactory("UserDB") '当前表所对应的表名称,方便后续筛选、追踪用。 Dim strTableName As String="物料表" '当前表中能够唯一定位行的主键字段名,如果表格中是组合主键,那么需要另外自定义写代码来生成日志信息了, '现成的GetChangesInfo方法满足不了多组合主键的场景。 Dim strPrimaryKeyName As String="物料编号" '获取一个空的日志表,方便新增记录 Dim dtLog As DataTableHelp =db.ExecuteDataTableHelp("select * From 修改日志 where 1=2",True) ''方法二:我们可以通过设置第三个fillData参数为False来不加载任何数据,而不用在SQL语句中添加where筛选条件 'dtLog=db.ExecuteDataTableHelp("select * From 修改日志",True,False) '直接获得修改信息 Dim info As ChangesInfo =tbl.GetChangesInfo(strPrimaryKeyName) '这里传入的参数为主键字段名 '遍历所有有修改的记录 For Each Item As KeyValuePair(Of String,String) In info.Modified Dim dr As RowData=dtLog.AddNew() dr("表名称")=strTableName dr("主键字段名")=strPrimaryKeyName dr("修改时间")=Proj.SysTime.Now dr("修改者")=Proj.User.UserID dr("修改内容")=Item.Value '这里是修改的记录信息 dr("修改主键值")=Item.Key '这里是主键值 dr("修改类型")="修改" Next '遍历所有新增的记录 For Each Item As KeyValuePair(Of String,String) In info.Added Dim dr As RowData=dtLog.AddNew() dr("表名称")=strTableName dr("主键字段名")=strPrimaryKeyName dr("修改时间")=Proj.SysTime.Now dr("修改者")=Proj.User.UserID dr("修改内容")=Item.Value '这里是修改的记录信息 dr("修改主键值")=Item.Key '这里是主键值 dr("修改类型")="新增" Next '遍历所有删除的记录 For Each Item As KeyValuePair(Of String,String) In info.Added Dim dr As RowData=dtLog.AddNew() dr("表名称")=strTableName dr("主键字段名")=strPrimaryKeyName dr("修改时间")=Proj.SysTime.Now dr("修改者")=Proj.User.UserID dr("修改内容")=Item.Value '这里是修改的记录信息 dr("修改主键值")=Item.Key '这里是主键值 dr("修改类型")="删除" Next'因为是新增记录,我们有一个更快的保存方案 If db.SupportSqlBurkCopy Then '使用SqlBurkCopy可以大大提高保存纯新增数据的速度 db.SqlBurkCopy(dtLog.DataTable,"修改日志") Else dtLog.Save() End If |
C# |
// 为了演示,取当前表 SmGrid tbl = Proj.CurrentSmGrid; // 日志要保存的数据源 Database db = Proj.SysDataFactory["UserDB"]; // 当前表所对应的表名称,方便后续筛选、追踪用。 string strTableName = "物料表"; // 当前表中能够唯一定位行的主键字段名,如果表格中是组合主键,那么需要另外自定义写代码来生成日志信息了, // 现成的GetChangesInfo方法满足不了多组合主键的场景。 string strPrimaryKeyName = "物料编号"; // 获取一个空的日志表,方便新增记录 DataTableHelp dtLog = db.ExecuteDataTableHelp("select * From 修改日志 where 1=2", true); // 方法二:我们可以通过设置第三个fillData参数为False来不加载任何数据,而不用在SQL语句中添加where筛选条件 // dtLog=db.ExecuteDataTableHelp("select * From 修改日志",true,false); // 直接获得修改信息 ChangesInfo info = tbl.GetChangesInfo(strPrimaryKeyName); // 这里传入的参数为主键字段名 // 遍历所有有修改的记录 foreach (KeyValuePair<string, string> Item in info.Modified) { RowData dr = dtLog.AddNew(); dr["表名称"] = strTableName; dr["主键字段名"] = strPrimaryKeyName; dr["修改时间"] = Proj.SysTime.Now; dr["修改者"] = Proj.User.UserID; dr["修改内容"] = Item.Value; // 这里是修改的记录信息 dr["修改主键值"] = Item.Key; // 这里是主键值 dr["修改类型"] = "修改"; } // 遍历所有新增的记录 foreach (KeyValuePair<string, string> Item in info.Added) { RowData dr = dtLog.AddNew(); dr["表名称"] = strTableName; dr["主键字段名"] = strPrimaryKeyName; dr["修改时间"] = Proj.SysTime.Now; dr["修改者"] = Proj.User.UserID; dr["修改内容"] = Item.Value; // 这里是修改的记录信息 dr["修改主键值"] = Item.Key; // 这里是主键值 dr["修改类型"] = "新增"; } // 遍历所有删除的记录 foreach (KeyValuePair<string, string> Item in info.Added) { RowData dr = dtLog.AddNew(); dr["表名称"] = strTableName; dr["主键字段名"] = strPrimaryKeyName; dr["修改时间"] = Proj.SysTime.Now; dr["修改者"] = Proj.User.UserID; dr["修改内容"] = Item.Value; // 这里是修改的记录信息 dr["修改主键值"] = Item.Key; // 这里是主键值 dr["修改类型"] = "删除"; }// 因为是新增记录,我们有一个更快的保存方案 if (db.SupportSqlBurkCopy) // 使用SqlBurkCopy可以大大提高保存纯新增数据的速度 db.SqlBurkCopy(dtLog.DataTable,"修改日志"); else dtLog.Save(); |
GetChangesInfo方法返回的修改信息是固定的,而且也不能处理组合主键的场景,而每个人对修改日志的理解不一样,想保存的数据格式也不一样。所以我们又另外提供一个实现的代码示例,更方便大家自己修改,也能实现更强大的功能。
Vb.Net |
|
C# |
// 为了演示,取当前表 SmGrid tbl = Proj.CurrentSmGrid; // 当前表所对应的表名称,方便后续筛选、追踪用。 string primaryKeyName = "ItemNO"; // 获得修改记录 DataTableHelp dtChanges = tbl.DataTableHelp.DataTable.GetChanges(DataRowState.Modified).GetDataTableHelp(true); if (dtChanges != null && dtChanges.DataRows.Count > 0) { foreach (RowData dr in dtChanges.DataRows) { string strPrimaryKey = dr[primaryKeyName].CType<string>(""); StringBuilder sb = new StringBuilder(); sb.Append("当前行列【" + primaryKeyName + "】的值为:【" + strPrimaryKey + "】"); foreach (ColData dc in dtChanges.DataCols) { // 如果初始值与当前值不一样 if (dr.BaseRow[dc.Name, DataRowVersion.Original].CType<string>("") != dr.BaseRow[dc.Name, DataRowVersion.Current].CType<string>("")) { sb.Append("列【" + dc.Name + "】从原始数据【" + dr[dc.Name, DataRowVersion.Original].CType<string>("") + "】修改为【" + dr[dc.Name, DataRowVersion.Current].CType<string>("") + "】"); } } // 输出修改行信息 Proj.MsgDebug.Add(sb.ToString()); } } // 获得新增行记录 DataTableHelp dtAdd = tbl.DataTableHelp.DataTable.GetChanges(DataRowState.Added).GetDataTableHelp(true); if (dtAdd != null && dtAdd.DataRows.Count > 0) { foreach (RowData dr in dtAdd.DataRows) { string strPrimaryKey = dr[primaryKeyName].CType<string>(""); // 输出新增行信息 if (!string.IsNullOrEmpty(strPrimaryKey)) { string strAdd = "新增行,列【" + primaryKeyName + "】的值为:【" + strPrimaryKey + "】"; Proj.MsgDebug.Add(strAdd); } } } // 删除行记录 DataTable dtDelete = tbl.DataTableHelp.DataTable.GetChanges(DataRowState.Deleted); if (dtDelete != null && dtDelete.Rows.Count > 0) { // 先获得列名称信息 string strCols = ""; foreach (DataColumn col in dtDelete.Columns) strCols = strCols + "|" + col.ColumnName; strCols = strCols.Trim("|"); foreach (DataRow dr in dtDelete.Rows) { string strPrimaryKey = dr[primaryKeyName, DataRowVersion.Original].CType<string>(""); StringBuilder sb = new StringBuilder(); sb.Append("删除行,列【" + primaryKeyName + "】的值为:【" + strPrimaryKey + "】"); sb.Append(";列名称为:" + strCols); sb.Append(";当前行的值为:"); foreach (DataColumn dc in dtDelete.Columns) { if (dr.IsNull(dc, DataRowVersion.Original)) { sb.Append(" |"); } else { sb.Append(dr[dc.ColumnName, DataRowVersion.Original].CType<string>("") + "|"); } } // 输出删除行信息 if (!string.IsNullOrEmpty(strPrimaryKey)) { Proj.MsgDebug.Add(sb.ToString().Substring(0, sb.ToString().Length - 1)); } } } // 返回结果:当前行列【ItemNO】的值为:【WL-R-000001】列【Size】从原始数据【125】修改为【250】 // 返回结果:当前行列【ItemNO】的值为:【WL-B-000002】列【Size】从原始数据【125】修改为【111】 // 返回结果:新增行,列【ItemNO】的值为:【WL-G-000002】 // 返回结果:删除行,列【ItemNO】的值为:【WL-Y-000001】;列名称为:_LockRowFlag|_SortFlag|_IdentifyFlag|ItemNO|ItemName|Color|Size|Number|SizeItemNO;当前行的值为: |4|7|WL-Y-000001|黄色衣服|Yellow|125| |WL-Y-000001 |