实现效果: 对任意一个数据库的实体类,很轻松的使用一个公共方法,添加对实体类修改的日志
实现思路:对任意实体类,添加一个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) 获取自定义特性,也就是获取在数据库的表名,如果不存在就获取本身的类名