C# MVC 统一修改日志的添加

本文介绍了一种在实体类发生变更时自动记录修改日志的方法。通过自定义特性、覆盖SaveChanges方法及异步线程,实现了对实体类修改历史的跟踪与记录。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现效果: 对任意一个数据库的实体类,很轻松的使用一个公共方法,添加对实体类修改的日志

实现思路:对任意实体类,添加一个MVC特性Attribute,当实体类添加或修改的时候,执行一个公共方法,检查是否有这个特性,如果有并且实体类对象状态EntityState是修改状态,开启一个线程,进行日志的插入。

1 首先建立修改日志类

    [Table("AuditLog")]
    public class AuditLog 
    {
        public virtual int ID { get; set; }
        public virtual DateTime CreateTime { get; set; }
        public int ModelId { get; set; }
        public string UserName { get; set; }
        public string ModuleName { get; set; }
        public string TableName { get; set; }
        public string EventType { get; set; }
        public string NewValues { get; set; }
    }

日志类的属性包括

修改时间,修改实体类ID,操作人名称,修改方法名,修改类名,修改类型,修改后的实体类的所有值。

2 建立一个关于日志数据库的上下文类上下文类LogDbContext

    public class LogDbContext : DbContextBase, IAuditable
    {
        public LogDbContext()
            : base(CachedConfigContext.Current.DaoConfig.Log)
        {
            Database.SetInitializer<LogDbContext>(null);
        }

        public DbSet<AuditLog> AuditLogs { get; set; }
    }

3 创建一个静态方法WriteLog用来添加日志数据,并且利用JsonConvert.SerializeObject把最新修改的值对象NewValues转化为JSON数据

    public class LogDbContext : DbContextBase, IAuditable
    {
        public LogDbContext()
            : base(CachedConfigContext.Current.DaoConfig.Log)
        {
            Database.SetInitializer<LogDbContext>(null);
        }

        public DbSet<AuditLog> AuditLogs { get; set; }

        public void WriteLog(int modelId, string userName, string moduleName, string tableName, string eventType, ModelBase newValues)
        {
            this.AuditLogs.Add(new AuditLog()
            {
                ModelId = modelId,
                UserName = userName,
                ModuleName = moduleName,
                TableName = tableName,
                EventType = eventType,
                NewValues = JsonConvert.SerializeObject(newValues, new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore })
            });

            this.SaveChanges();
            this.Dispose();
        }
    }

4 添加一个自定义特性,当检查到类有这个特性,就记录它的修改日志

    public class AuditableAttribute : Attribute
    {
    }

5 添加共用父类上下文DbContextBase,覆盖SaveChanges方法,在每次保存调用SaveChanges时候,在调用检查并插入日志方法WriteAuditLog

    /// DAL基类,实现Repository通用泛型数据访问模式
    /// </summary>
    public class DbContextBase : DbContext, IDataRepository, IDisposable
    {

        public DbContextBase(string connectionString, IAuditable auditLogger)
            : this(connectionString)
        {
            this.AuditLogger = auditLogger;
        }
        public override int SaveChanges()
        {
            this.WriteAuditLog(); 
            var result = base.SaveChanges();
            return result;
        }

        internal void WriteAuditLog()
        {
            if (this.AuditLogger == null)
                return;

            foreach (var dbEntry in this.ChangeTracker.Entries<ModelBase>().Where(p => p.State == EntityState.Added || p.State == EntityState.Deleted || p.State == EntityState.Modified))
            {
                var auditableAttr = dbEntry.Entity.GetType().GetCustomAttributes(typeof(AuditableAttribute), false).SingleOrDefault() as AuditableAttribute;
                if (auditableAttr == null)
                    continue;

                var operaterName = WCFContext.Current.Operater.Name;

                Task.Factory.StartNew(() =>
                {
                    var tableAttr = dbEntry.Entity.GetType().GetCustomAttributes(typeof(TableAttribute), false).SingleOrDefault() as TableAttribute;
                    string tableName = tableAttr != null ? tableAttr.Name : dbEntry.Entity.GetType().Name;
                    var moduleName = dbEntry.Entity.GetType().FullName.Split('.').Skip(1).FirstOrDefault();

                    this.AuditLogger.WriteLog(dbEntry.Entity.ID, operaterName, moduleName, tableName, dbEntry.State.ToString(), dbEntry.Entity);
                });
            }

        }
    }

6 在构造函数添加一个日志数据库参数,继承它的实体类可以引入日志数据库

   public DbContextBase(string connectionString, IAuditable auditLogger)
            : this(connectionString)
        {
            this.AuditLogger = auditLogger;
        }

7 WriteAuditLog方法,先检查日志数据库是否引入

      if (this.AuditLogger == null)
                return;

6 WriteAuditLog方法,再检查实体类状态EntityState,是否是修改或者新增状态

       foreach (var dbEntry in this.ChangeTracker.Entries<ModelBase>().Where(p => p.State == EntityState.Added || p.State == EntityState.Deleted || p.State == EntityState.Modified))
            {
 }

7 再判断自定义特性AuditLogger是否存在,

var auditableAttr = dbEntry.Entity.GetType().GetCustomAttributes(typeof(AuditableAttribute), false).SingleOrDefault() as AuditableAttribute;
                if (auditableAttr == null)
                    continue;

8 如果一切通过,开启线程插入日志

       Task.Factory.StartNew(() =>
                {
                    var tableAttr = dbEntry.Entity.GetType().GetCustomAttributes(typeof(TableAttribute), false).SingleOrDefault() as TableAttribute;
                    string tableName = tableAttr != null ? tableAttr.Name : dbEntry.Entity.GetType().Name;
                    var moduleName = dbEntry.Entity.GetType().FullName.Split('.').Skip(1).FirstOrDefault();

                    this.AuditLogger.WriteLog(dbEntry.Entity.ID, operaterName, moduleName, tableName, dbEntry.State.ToString(), dbEntry.Entity);
                });

9 dbEntry.Entity.GetType().GetCustomAttributes(typeof(TableAttribute) 获取自定义特性,也就是获取在数据库的表名,如果不存在就获取本身的类名

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值