Winform开发框架之权限管理系统改进的经验总结(4)-一行代码实现表操作日志记录
在前面介绍了几篇关于我的权限系统改进的一些经验总结,本篇继续这一系列主体,介绍如何一行代码实现重要表的操作日志记录。我们知道,在很多业务系统里面,数据是很敏感的,特别对于一些增加、修改、删除等关键的操作,如果能在框架层面的支持基础上,以最少的代码实现重要表的日志记录,那么是一件非常值得庆贺的事情,也能够为我们客户的数据提供重要的日志跟踪,甚至是数据恢复的参考。
1、数据访问层的对象继承关系
首先,为了减少重复代码的编写,合理的继承关系是必要的,我们需要在数据访问层上建立合理的继承关系,如下是我的Winform开发框架的继承关系。每个数据访问对象(如ItemDetail数据访问对象)都继承一个抽象基类AbstractBaseDAL和一个IBaseDAL基类接口,同时它也有自己特殊的业务接口,如IItemDetail,关系如下所示。
有了上面的继承关系,我们就可以把常规的数据库重要操作(增删改)放到一个高一级的层次上去解决这个问题,而不需要在每个数据访问层的业务类来实现。
2、操作日志记录事件的定义和使用
为了更好实现数据操作日志的记录,我们以事件方式来触发操作日志的记录,事件的具体记录实现,可以交给外部来记录处理。如果事件被外部赋值了,那么就可以在底层触发这个事件记录,记录事件的定义代码在抽象基类进行定义,如下所示。
    /// 定义一个记录操作日志的事件处理
    /// </summary>
    /// <param name="userId">操作的用户ID</param>
    /// <param name="tableName">操作表名称</param>
    /// <param name="operationType">操作类型:增加、修改、删除</param>
    /// <param name="note">操作的详细记录信息</param>
    /// <returns></returns>
    public delegate bool OperationLogEventHandler(string userId, string tableName, string operationType, string note, DbTransaction trans = null);
    /// <summary>
    /// 数据访问层的超级基类,所有数据库的数据访问基类都继承自这个超级基类,包括Oracle、SqlServer、Sqlite、MySql、Access等
    /// </summary>
    public abstract class AbstractBaseDAL<T> where T : BaseEntity, new()
    {
        #region 构造函数
        protected string dbConfigName = ""; //数据库配置名称
        protected string parameterPrefix = "@";//数据库参数化访问的占位符
        protected string safeFieldFormat = "[{0}]";//防止和保留字、关键字同名的字段格式,如[value]
        protected string tableName;//需要初始化的对象表名
        protected string primaryKey;//数据库的主键字段名
        protected string sortField;//排序字段
        protected bool isDescending = true;//是否为降序
        protected string selectedFields = " * ";//选择的字段,默认为所有(*)       
        public event OperationLogEventHandler OnOperationLog;//定义一个操作记录的事件处理


.....................
以上是抽象基类AbstractBaseDAL的部分代码,上面代码定义了一个操作记录的委托和事件对象来处理操作日志的记录,通过委托的定义,我们可以规定具体的事件接口定义,并在抽象基类的底层构造这些参数的数值,传递给外部的对象进行处理。
那么我们是如何在底层操作构造这些信息的呢?
其实就是在相应的重要操作接口函数上调用这个定义的事件。我们可以在抽象基类的插入、修改、删除等接口上调用事件进行处理即可,为了更好处理相关数据的构造逻辑,我们把调用OnOperationLog的事件封装到一个单独的函数里面进行处理,如下所示是底层更新操作的代码,通过增加一个OperationLogOfUpdate来实现数据日志的事件处理。
        /// <summary>
        /// 更新对象属性到数据库中
        /// </summary>
        /// <param name="obj">指定的对象</param>
        /// <param name="primaryKeyValue">主键的值</param>
        /// <param name="trans">事务对象</param>
        /// <returns>执行成功返回<c>true</c>,否则为<c>false</c></returns>
        public virtual bool Update(T obj, object primaryKeyValue, DbTransaction trans = null)
        {
            ArgumentValidation.CheckForNullReference(obj, "传入的对象obj为空");
            OperationLogOfUpdate(obj, primaryKeyValue, trans);//根据设置记录操作日志
            Hashtable hash = GetHashByEntity(obj);
            return Update(primaryKeyValue, hash, trans);
        }
然后我们在具体的事件处理封装函数OnOperationLog的里面添加处理逻辑即可,一般事件的标准处理为如下代码。
        /// <summary>
        /// 修改操作的日志记录
        /// </summary>
        /// <param name="id">记录ID</param>
        /// <param name="obj">数据对象</param>
        /// <param name="trans">事务对象</param>
        protected virtual void OperationLogOfUpdate(T obj, object id, DbTransaction trans = null)
        {
            if (OnOperationLog != null)
            {
                    ...............................//构造相关参数
                    OnOperationLog(userId, this.tableName, operationType, note, trans);
                }
            }
        }
我们知道,一般操作日志都会记录是谁进行操作的,然后把它写到日志里面,并把操作的内容可读化即可,那么在更新的时候,我们如何知道是谁操作的对象呢?因为我们没有传递具体的用户ID等标识的啊。
这个问题挺头痛,如果增加多一个参数,那么就得修改很多相关的调用逻辑,这个明显不太符合我们简约的风格,因此最好另寻其他方式来实现这个人员身份记录的问题。
我们知道,一般插入、更新操作,都是带一个操作对象的,这个操作对象是一个实体类,基类是BaseEntity,那么我们可以在它的身上定义多一个属性,这个属性不参数数据的保存,只是作为参数的传递和识别而已,实体类基类的代码如下所示。
    /// <summary>
    /// 框架实体类的基类
    /// </summary>
    [DataContract]
    public class BaseEntity
    {
        private string m_CurrentLoginUserId;
        /// <summary>
        /// 当前登录用户ID。该字段不保存到数据表中,只用于记录用户的操作日志。
        /// </summary>
        [DataMember]
        public string CurrentLoginUserId
        {
            get { return m_CurrentLoginUserId; }
            set { m_CurrentLoginUserId = value; }
        }
    }
}
validation框架有了这个信息,我们就可以在刚才的事件处理逻辑上进行获取用户的ID操作了。
string userId = obj.CurrentLoginUserId;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。