You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#版MongoDB Driver中实现软删除全局过滤器?

MongoDB .NET Driver全局软删除过滤器实现方案

首先明确:MongoDB官方.NET驱动没有原生支持类似EF Core的全局查询过滤器功能,你可以通过以下几种主流方案实现需求:


方案1:统一封装仓储层(推荐)

这是目前行业内使用率最高的方案,把所有Mongo集合操作封装在仓储基类中,默认对符合软删除约定的实体追加IsActive = true过滤条件,特殊场景可通过参数关闭过滤。

// 软删除实体统一实现该接口
public interface ISoftDeletable
{
    bool IsActive { get; }
}

// 仓储基类
public class BaseRepository<T> where T : class
{
    private readonly IMongoCollection<T> _collection;

    public BaseRepository(IMongoDatabase database, string collectionName)
    {
        _collection = database.GetCollection<T>(collectionName);
    }

    public async Task<List<T>> GetListAsync(FilterDefinition<T> filter = null, bool includeDeleted = false, CancellationToken cancellationToken = default)
    {
        var finalFilter = filter ?? Builders<T>.Filter.Empty;
        // 实体实现软删除接口且不包含已删除数据时,追加过滤条件
        if (!includeDeleted && typeof(ISoftDeletable).IsAssignableFrom(typeof(T)))
        {
            finalFilter &= Builders<T>.Filter.Eq(nameof(ISoftDeletable.IsActive), true);
        }
        return await _collection.Find(finalFilter).ToListAsync(cancellationToken);
    }

    // 其他查询方法(GetById、Count、Aggregate等)都按照相同逻辑追加过滤
    // 封装删除方法默认走软删除,避免物理删除数据
    public async Task SoftDeleteAsync(string id, CancellationToken cancellationToken = default)
    {
        if (typeof(ISoftDeletable).IsAssignableFrom(typeof(T)))
        {
            var update = Builders<T>.Update.Set(nameof(ISoftDeletable.IsActive), false);
            await _collection.UpdateOneAsync(Builders<T>.Filter.Eq("Id", id), update, cancellationToken: cancellationToken);
        }
    }
}

// 你的实体类实现软删除接口即可生效
public class User : ISoftDeletable
{
    public string Id { get; private set; }
    public string Name { get; private set;} 
    public bool IsActive{ get; private set; }
}

该方案灵活度高,适配各种自定义过滤场景,也方便后续扩展其他全局规则。


方案2:基于驱动内置拦截器实现(MongoDB.Driver 2.18+版本支持)

2.18及以上版本的MongoDB .NET Driver提供了命令拦截器能力,可直接拦截所有发往数据库的查询命令,统一修改追加过滤条件,无需修改业务层代码。

public class SoftDeleteInterceptor : IMongoCommandInterceptor
{
    // 维护支持软删除的集合列表,可根据实际业务调整
    private readonly HashSet<string> _softDeleteCollections = new HashSet<string> { "Users" };

    public void AfterExecution<TResult>(CommandExecutionContext<TResult> context)
    {
        // 无需处理执行后逻辑
    }

    public void BeforeExecution(CommandExecutionContext context)
    {
        // 仅拦截查询类命令
        if (context.CommandName is "find" or "aggregate")
        {
            var command = context.Command;
            var collectionName = context.CommandName switch
            {
                "find" => command["find"].AsString,
                "aggregate" => command["aggregate"].AsString,
                _ => string.Empty
            };
            if (_softDeleteCollections.Contains(collectionName))
            {
                // 追加软删除过滤条件
                var softDeleteFilter = new BsonDocument("IsActive", true);
                if (command.Contains("filter"))
                {
                    var existingFilter = command["filter"].AsBsonDocument;
                    command["filter"] = new BsonDocument("$and", new BsonArray { existingFilter, softDeleteFilter });
                }
                else
                {
                    command["filter"] = softDeleteFilter;
                }
            }
        }
    }
}

配置拦截器到客户端即可全局生效:

var clientSettings = MongoClientSettings.FromConnectionString("你的数据库连接串");
clientSettings.CommandInterceptors.Add(new SoftDeleteInterceptor());
var mongoClient = new MongoClient(clientSettings);

该方案侵入性极低,缺点是特殊场景需要绕过拦截的逻辑实现比较繁琐,会带来少量性能损耗。


方案3:扩展方法封装

如果你不想引入仓储层,可以编写IMongoCollection的扩展方法,统一封装查询逻辑,业务层约定使用扩展方法查询即可:

public static class MongoCollectionExtensions
{
    public static IFindFluent<T, T> FindWithSoftDelete<T>(this IMongoCollection<T> collection, FilterDefinition<T> filter, bool includeDeleted = false)
        where T : ISoftDeletable
    {
        var finalFilter = filter;
        if (!includeDeleted)
        {
            finalFilter &= Builders<T>.Filter.Eq(x => x.IsActive, true);
        }
        return collection.Find(finalFilter);
    }
}

该方案改动小、实现简单,缺点是依赖团队开发规范约束,容易出现漏用扩展方法导致过滤失效的问题。

提示:无论采用哪种方案,都建议统一封装删除操作,默认走软删除更新IsActive为false,避免误操作物理删除数据。

内容的提问来源于stack exchange,提问作者Diana Barros

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 16:45:03