EF Core – QueryFilter & Interception

主要参考

Global Query Filters

Interceptors

 

QueryFilter

QueryFilter 就是默认过滤, 非常适合用来做 Soft Delete

builder.HasQueryFilter(e => EF.Property<DateTimeOffset?>(e, "DateDeleted") == null);

设置这个以后, 一般的 query 语句就拿不到 deleted 的 row 了

如果想获取 deleted row 那么就需要通过 IgnoreQueryFilters 来 by pass 它.

blogs = db.Blogs
    .Include(b => b.Posts)
    .IgnoreQueryFilters()
    .ToList();

 

Interception

Interception 也适合用来做 Soft Delete 或者简单的 Audit Trail

但是还有一个更简单的做法是直接 override SaveChangesAsync 方法

去 DbContext class

public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
    foreach (var entry in ChangeTracker.Entries())
    {
        if (entry.State == EntityState.Added)
        {
                    
        }
        else if (entry.State == EntityState.Modified)
        {

        }
        else if (entry.State == EntityState.Deleted)
        {

        }
        var tableName = entry.Metadata.GetTableName();

        foreach (var property in entry.Properties)
        {
            var isModified = property.IsModified;
            var originalValue = property.OriginalValue;
            var currentValue = property.CurrentValue;
            var metadata = property.Metadata;
        }
    }
    return base.SaveChangesAsync(cancellationToken);
}

获取 Entry 资料, 然后修改 Entry 就可以操控最终 save 的结构了. (比如把 Deleted 换成 Modified)

创建一个 Interceptor (我这里用 SaveChangesInterceptor 举例)

public class SoftDeleteInterception : SaveChangesInterceptor
{
    public override ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
    {
        var entities = eventData.Context!.ChangeTracker.Entries();
        return new ValueTask<InterceptionResult<int>>(result);
    }
}

注: SavingChangesAsync 是 before SQL, SavedChangesAsync 是 after success SQL, FailedChangesAsync 是 after fail SQL.

在 DbContext class register 这个 interceptor

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.AddInterceptors(new SoftDeleteInterception());

Change State in SaveChanges

看注释, 有些逻辑和平时会不同,比如 set value 时, 不会 update IsModified (但 generate 出来的语句还是有的, 我想可能 EF 最后还会再对比一次吧...懒惰去研究了)

foreach (var entry in eventData.Context!.ChangeTracker.Entries())
{
    var entityType = entry.Entity.GetType();
    if (entityType.FullName == "TestEFCore.Product")
    {
        entry.State = EntityState.Unchanged; // if change to modified then all properties will become IsModified 
        typeof(Product).GetProperty("Name")!.SetValue(entry.Entity, "New Value"); // current value 会 update, but IsModified 依然是 false
        entry.Property("DateDeleted").CurrentValue = DateTimeOffset.Now;
        entry.Property("DateDeleted").IsModified = true; // will update entry.State to modified
        foreach (var p in entry.Properties)
        {
            var isModified = p.IsModified;
        }
        var state = entry.State;
    }
}

Dependency Injection inside Interceptor

参考:

Ability to register IInterceptor without an IDbContextOptionsExtension

A better way of resolving EF Core interceptors with dependency injection

 

posted @ 2021-10-19 23:55  兴杰  阅读(358)  评论(0编辑  收藏  举报