导读:本期聚焦于小伙伴创作的《Dapper如何实现软删除查询 Dapper全局查询过滤器思路》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《Dapper如何实现软删除查询 Dapper全局查询过滤器思路》有用,将其分享出去将是对创作者最好的鼓励。

在业务开发中,软删除是比物理删除更常用的数据删除方式,通常我们会在表中增加一个is_deleted字段,删除时将该字段设为1,查询时只返回is_deleted为0的记录。Dapper作为轻量级ORM,本身没有内置的全局查询过滤器功能,需要开发者自行实现统一的软删除查询逻辑。

Dapper如何实现软删除查询 Dapper全局查询过滤器思路

软删除的基础实现

首先我们来看最基础的软删除查询写法,假设我们有一张user表,包含id、name、is_deleted三个字段,查询未删除用户的SQL如下:

-- 查询未删除的用户
SELECT id, name FROM user WHERE is_deleted = 0;

如果使用Dapper执行这个查询,代码可以写成:

using Dapper;
using System.Data.SqlClient;
using System.Linq;

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class UserRepository
{
    private readonly string _connectionString;

    public UserRepository(string connectionString)
    {
        _connectionString = connectionString;
    }

    public List<User> GetActiveUsers()
    {
        using (var conn = new SqlConnection(_connectionString))
        {
            string sql = "SELECT id, name FROM user WHERE is_deleted = 0";
            return conn.Query<User>(sql).ToList();
        }
    }
}

这种方式的问题是,每次查询user表都需要手动加上is_deleted = 0的条件,如果表数量多或者查询场景复杂,会产生大量重复代码,也容易遗漏条件导致查询到已删除的数据。

Dapper全局查询过滤器实现思路

思路一:封装通用查询方法

我们可以封装一个通用的查询方法,自动为所有标记了软删除特性的实体拼接过滤条件。首先定义一个标识软删除的接口:

public interface ISoftDelete
{
    int Is_Deleted { get; set; }
}

让我们的实体类实现这个接口:

public class User : ISoftDelete
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Is_Deleted { get; set; }
}

然后封装一个通用的查询方法,通过反射判断实体是否实现了ISoftDelete接口,如果是则自动拼接过滤条件:

using Dapper;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;

public class DapperRepository
{
    private readonly string _connectionString;

    public DapperRepository(string connectionString)
    {
        _connectionString = connectionString;
    }

    public List<T> Query<T>(string sql, object param = null)
    {
        using (var conn = new SqlConnection(_connectionString))
        {
            // 判断T是否实现了ISoftDelete接口
            var isSoftDelete = typeof(ISoftDelete).IsAssignableFrom(typeof(T));
            if (isSoftDelete)
            {
                // 检查sql中是否已经包含is_deleted条件,避免重复拼接
                if (!sql.Contains("is_deleted", StringComparison.OrdinalIgnoreCase))
                {
                    // 简单处理,实际场景需要根据sql结构拼接,比如where后面加条件
                    if (sql.Contains("WHERE", StringComparison.OrdinalIgnoreCase))
                    {
                        sql += " AND is_deleted = 0";
                    }
                    else
                    {
                        sql += " WHERE is_deleted = 0";
                    }
                }
            }
            return conn.Query<T>(sql, param).ToList();
        }
    }
}

使用时只需要调用封装好的Query方法,不需要手动写过滤条件:

var repo = new DapperRepository("your_connection_string");
// 这里不需要手动加is_deleted条件,方法会自动拼接
var users = repo.Query<User>("SELECT id, name FROM user");

思路二:使用SQL拦截器动态修改SQL

如果需要更灵活的处理,比如支持多表查询、复杂SQL的自动过滤,可以使用Dapper的拦截器功能,在SQL执行前动态修改SQL文本。首先实现一个DbCommandInterceptor

using Dapper;
using System;
using System.Data;
using System.Data.Common;
using System.Text.RegularExpressions;

public class SoftDeleteInterceptor : DbCommandInterceptor
{
    public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        // 判断当前查询的表是否包含需要软删除过滤的表,这里可以维护一个需要过滤的表名列表
        var needFilterTables = new List<string> { "user", "order" };
        foreach (var table in needFilterTables)
        {
            // 简单正则匹配,实际场景需要更严谨的SQL解析
            if (Regex.IsMatch(command.CommandText, $@"b{table}b", RegexOptions.IgnoreCase))
            {
                if (!command.CommandText.Contains("is_deleted", StringComparison.OrdinalIgnoreCase))
                {
                    if (command.CommandText.Contains("WHERE", StringComparison.OrdinalIgnoreCase))
                    {
                        command.CommandText += $" AND {table}.is_deleted = 0";
                    }
                    else
                    {
                        command.CommandText += $" WHERE {table}.is_deleted = 0";
                    }
                }
            }
        }
        base.ReaderExecuting(command, interceptionContext);
    }
}

然后在使用Dapper的时候注册这个拦截器:

using Dapper;
using System.Data.SqlClient;

public class DbContext
{
    private static bool _interceptorRegistered = false;

    public static SqlConnection GetConnection(string connectionString)
    {
        var conn = new SqlConnection(connectionString);
        if (!_interceptorRegistered)
        {
            // 注册拦截器
            SqlConnection.ClearAllPools();
            var interceptor = new SoftDeleteInterceptor();
            DbInterception.Add(interceptor);
            _interceptorRegistered = true;
        }
        return conn;
    }
}

注意事项

  • 动态拼接SQL的时候要注意SQL注入问题,上面的示例只是简单演示,实际场景需要更严谨的SQL解析逻辑,避免拼接恶意内容。
  • 如果表名或者字段名是数据库关键字,拼接条件的时候需要加上对应的转义符,比如MySQL用反引号,SQL Server用方括号。
  • 对于更新、删除操作,如果需要同时处理软删除逻辑,也可以在通用方法中做对应的处理,比如删除操作自动转为更新is_deleted字段。
  • 如果项目中有部分查询需要包含已删除的数据,可以加一个开关参数,在调用查询方法的时候传入,跳过自动拼接过滤条件。

总结

Dapper虽然没有内置全局查询过滤器,但是通过封装通用查询方法或者SQL拦截器的方式,都可以实现软删除的统一查询过滤。开发者可以根据项目的复杂程度选择合适的实现方式,简单的项目用封装通用方法的方式就足够,复杂的多表查询场景可以考虑用SQL拦截器实现更灵活的逻辑,最终减少重复代码,保证查询逻辑的一致性。

Dapper软删除全局查询过滤器ORM修改时间:2026-07-10 17:30:35

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。