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 @   兴杰  阅读(428)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· 百万级群聊的设计实践
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
点击右上角即可分享
微信分享提示