以下都是干货,不整那没用的
软删除的作用这里就不说了,懂得都懂。
审计就是记录实体最后一次被修改的时间,修改人的id,创建的时间,创建人的id,删除的时间,删除的人的id
IAuditInsert.cs
///
/// 审计添加接口
///
public interface IAuditInsert
{
long? CreatorUserId { get; set; }
DateTime CreationTime { get; set; }
}
IAuditModif.cs
///
/// 审计修改接口
///
public interface IAuditModif
{
public DateTime? LastModificationTime { get; set; }
public long? LastModifierUserId { get; set; }
}
IEntity.cs
///
/// 实体基类接口
///
/// 主键
public interface IEntity<TPrimaryKey> : IEntity
{
TPrimaryKey Id { get; set; }
}
public interface IEntity
{
}
AuditInsertEntity.cs
///
/// 审计添加基类
///
/// 主键类型
public class AuditInsertEntity<TPrimaryKey> :Entity<TPrimaryKey> , IAuditInsert
{
public long? CreatorUserId { get; set; }
public DateTime CreationTime { get ; set ; }
}
AuditModifEntity.cs
///
/// 审计修改基类
///
/// 主键类型
public class AuditModifEntity<TPrimaryKey> : Entity<TPrimaryKey>,IAuditModif
{
public DateTime? LastModificationTime { get; set; }
public long? LastModifierUserId { get; set; }
}
Entity.cs
///
/// 实体基类泛型
///
/// 主键类型
public abstract class Entity<TPrimaryKey> : IEntity<TPrimaryKey>
{
public TPrimaryKey Id { get; set; }
}
///
/// 实体基类
///
public class Entity : IEntity
{
public int Id { get; set; }
}
AuditdEntity.cs
///
/// 基础审计实体
///
///
public abstract class AuditdEntity<TPrimaryKey> : AuditInsertEntity<TPrimaryKey>, IAuditModif
{
public DateTime? LastModificationTime { get ; set ; }
public long? LastModifierUserId { get ; set ; }
}
这里,我将软删除与审计直接结合了,严谨一点的话,应该只有一个
IsDeleted才对,DeletionTime和DeleterUserId属于审计范畴内。
ISoftDelete.cs
///
/// 软删除接口
///
public interface ISoftDelete
{
bool IsDeleted { get; set; }
DateTime? DeletionTime { get; set; }
long? DeleterUserId { get; set; }
}
AuditSoftDelete.cs
///
/// 软删除基类
///
public abstract class AuditSoftDelete : ISoftDelete
{
bool IsDeleted { get; set; }
DateTime? DeletionTime { get ; set ; }
long? DeleterUserId { get ; set ; }
}
IFullAuditedEntity .cs接口
///
/// 审计接口
///
///
public interface IFullAuditedEntity<TPrimaryKey> :IEntity<TPrimaryKey> ,IAuditInsert, IAuditModif, ISoftDelete
{
}
FullAuditedEntity.cs实现类
///
/// 全功能审计基类
///
///
public class FullAuditedEntity<TPrimaryKey> : AuditdEntity<TPrimaryKey>, IFullAuditedEntity<TPrimaryKey>, ISoftDelete
{
public bool IsDeleted { get; set ; }
public DateTime? DeletionTime { get; set; }
public long? DeleterUserId { get ; set ; }
}
SaveChangesAsync方法,在SaveChanges方法执行前,通过遍历被跟踪的实体,判断是否继承了我们写好的审计接口来做相应的处理。IHttpContextAccessor。这里建议通过属性注入的方式来获取IHttpContextAccessor。如果用的是构造函数注入,可能会出现很多问题。public class SystemDbContext:DbContext
{
public IHttpContextAccessor _httpContextAccessor { get; set; }
public SystemDbContext(DbContextOptions<CarRentalSystemDbContext> options):base(options)
{
}
#region Override
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ConfigurationQueryFilter();
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
ConfigurationOnBeforeSaving();
return base.SaveChanges(acceptAllChangesOnSuccess);
}
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken())
{
ConfigurationOnBeforeSaving();
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
#endregion
#region Private Method
private void ConfigurationOnBeforeSaving()
{
var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
var uid = long.Parse(userId);
foreach (var entry in ChangeTracker.Entries())
{
var entityType = entry.Entity.GetType();
switch (entry.State)
{
case EntityState.Deleted:
if (entry.Entity is ISoftDelete)
{
entry.State = EntityState.Modified;
entry.CurrentValues["IsDeleted"] = true;
entry.CurrentValues["DeleterUserId"] = uid;
entry.CurrentValues["DeletionTime"] = DateTime.Now;
}
break;
case EntityState.Modified:
if (entry.Entity is IAuditModif)
{
if (entry.Entity is IAuditInsert)
{
entry.Property("CreatorUserId").IsModified = false;
entry.Property("CreationTime").IsModified = false;
}
entry.CurrentValues["LastModifierUserId"] = uid;
entry.CurrentValues["LastModificationTime"] = DateTime.Now;
}
break;
case EntityState.Added:
if (entry.Entity is IEntity)
{
var idType = entry.CurrentValues["Id"]?.GetType();
if (idType != null && idType == typeof(Guid))
{
entry.CurrentValues["Id"] = Guid.NewGuid();
}
}
if (entry.Entity is IAuditInsert)
{
entry.CurrentValues["CreatorUserId"] = uid;
entry.CurrentValues["CreationTime"] = DateTime.Now;
}
break;
}
}
}
#endregion
}
实现思路
主要实现是判断当被Ef 跟踪的实体的状态为Deleted并实现了ISoftDelete接口时,我们将状态手动改为EntityState.Modified,然后将字段IsDeleted的值设置为true
自动过滤
在代码OnModelCreating方法中,有一个扩展方法modelBuilder.ConfigurationQueryFilter();是我们自己扩展的,我们在其中定义了一个全局过滤器,具体代码如下:
public static class DbContextQueryFilter
{
public static void ConfigurationQueryFilter(this ModelBuilder modelBuilder)
{
var entityTypes = modelBuilder.Model.GetEntityTypes();
foreach (var entityType in entityTypes)
{
if (typeof(ISoftDelete).IsAssignableFrom(entityType.ClrType))
{
var parameter = Expression.Parameter(entityType.ClrType, "del");
var body = Expression.Equal(Expression.Call(typeof(EF),
nameof(EF.Property), new[] { typeof(bool) }, parameter, Expression.Constant("IsDeleted")),
Expression.Constant(false));
modelBuilder.Entity(entityType.ClrType)
.HasQueryFilter(Expression.Lambda(body, parameter));
}
}
}
}
以下代码均在Web层
这里我们使用的是AutoFac第三方容器。
所需要的包:Autofac 和 Autofac.Extensions.DependencyInjection
新建一个 Autofac 的模块,将DbContext支持属性注入,使用PropertiesAutowired()
public class BusinessModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>();
builder.RegisterType<SystemDbContext>().PropertiesAutowired().InstancePerLifetimeScope();
}
}
Startup.cs中添加ConfigureContainer方法
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new BusinessModule());
}
你可以根据你的需要让你的实体类继承审计基类,如果你需要所有的审计功能,你可以像如下一样:
public class TestModel : FullAuditedEntity<Guid>
{
public string Msg { get; set; }
}
数据库中如下:
