跳到主要内容

过滤条件构建(DynamicFilter 与 FilterBuilder)

DynamicFilter 是 Chameleon 平台通用的动态查询条件模型(定义于 Chameleon.Common.Models,又称"动态过滤条件 / 查询条件 / where 条件"),用于 IChameleonDbContext 的 GetList / GetCount / IsExist 等查询方法。FilterBuilder 是平台提供的静态工厂类(Chameleon.Database 命名空间),用于以代码方式便捷构建 DynamicFilter。

概述​

DynamicFilter 是一棵条件树:叶子节点是「字段 + 操作符 + 值」,非叶子节点通过 Logic(And / Or)组合子条件。它同时被以下场景复用:

推荐始终用 FilterBuilder 构建条件,而不是手写 new DynamicFilter { ... },可避免操作符枚举写错。

DynamicFilter 模型属性​

属性类型含义取值
Fieldstring字段编码一律使用小写;会被转为小写并做 SQL 注入校验
OperatorFilterOperator操作符见下方操作符表
Valueobject比较值类型随操作符而定
ValueTypeFilterValueType值类型提示Default=0 / String=1 / Number=2 / DateTime=3 / Boolean=4 / Guid=5,默认 Default
LogicFilterLogic子条件逻辑And=0 / Or=1
FiltersList<DynamicFilter>子条件列表与当前节点为 And 关系

操作符总表​

FilterOperator 枚举与 FilterBuilder 工厂方法的对应关系(取值以后端枚举为准):

语义FilterBuilder 方法FilterOperator存储值Value 要求
等于Eq(field, value)Eq9任意
不等于Ne(field, value)Ne10任意
大于Gt(field, value)Gt11可比较类型
大于等于Gte(field, value)Gte12可比较类型
小于Lt(field, value)Lt13可比较类型
小于等于Lte(field, value)Lte14可比较类型
包含(like %v%)Contains(field, value)Ct1字符串
不包含NotContains(field, value)Nct4字符串
前缀匹配StartsWith(field, value)Sw2字符串
非前缀匹配NotStartsWith(field, value)Nsw5字符串
后缀匹配EndsWith(field, value)Ew3字符串
非后缀匹配NotEndsWith(field, value)New6字符串
在集合中In(field, value)In17逗号分隔字符串或数组
不在集合中NotIn(field, value)Nin18逗号分隔字符串或数组
数组包含(PostgreSQL @>)Act(field, value)Act19必须传 string[] 或 int[],不支持逗号分隔字符串
取全量安全条件FilterBuilder.Empty(静态属性)Ne10等价于 _id != Guid.Empty

组合方法:

static DynamicFilter And(params DynamicFilter[] filters); // 生成 Logic = And 的父节点
static DynamicFilter Or(params DynamicFilter[] filters); // 生成 Logic = Or 的父节点

使用示例​

单条件​

var filter = FilterBuilder.Eq("status", "Pending");
var list = await sdk.DbContext.GetListAsync("LeaveApplication", filter);

And 组合多条件​

var filter = FilterBuilder.And(
FilterBuilder.Eq("isdeleted", false),
FilterBuilder.Gte("amount", 1000),
FilterBuilder.Contains("orderno", "SO2026")
);

Or 与嵌套组合​

// (status = 'Approved' OR status = 'Rejected') AND isdeleted = false
var filter = FilterBuilder.And(
FilterBuilder.Or(
FilterBuilder.Eq("status", "Approved"),
FilterBuilder.Eq("status", "Rejected")
),
FilterBuilder.Eq("isdeleted", false)
);

In 与数组包含​

// In:逗号分隔字符串或数组均可
var inFilter = FilterBuilder.In("status", "Pending,Approved");
var inFilter2 = FilterBuilder.In("_id", someGuidArray);

// Act(数组字段包含):必须传数组,传逗号分隔字符串会抛 ArgumentException
var actFilter = FilterBuilder.Act("tags", new[] { "vip", "oversea" });

手写 DynamicFilter(不推荐但合法)​

var filter = new DynamicFilter
{
Logic = FilterLogic.And,
Filters = new List<DynamicFilter>
{
new() { Field = "status", Operator = FilterOperator.Eq, Value = "Pending" },
new() { Field = "isdeleted", Operator = FilterOperator.Eq, Value = false }
}
};

JSON 存储示例​

DynamicFilter 序列化为 JSON 后的存储/传输格式(接口传参、元数据存储位置均使用此结构):

{
"Field": "amount",
"Operator": "Gte",
"Value": 1000,
"ValueType": "Default",
"Logic": "And",
"Filters": [
{ "Field": "status", "Operator": "Eq", "Value": "Completed", "ValueType": "Default", "Logic": "And", "Filters": null }
]
}

行为细节与异常​

  • 字段名转小写:Field 会被 ToLowerInvariant() 后参与 SQL 生成,建议编码时就用小写
  • SQL 注入校验:字段名经 ValidateSqlInjection 校验,含非法字符抛 ArgumentException
  • ValueType 类型转换:ValueType 非 Default 时会尝试将 Value 转换为目标类型(如 "2026-01-01" 转 DateTime),转换失败抛 ArgumentException
  • Act 操作符:由自定义过滤器翻译为 PostgreSQL 的 field @> ARRAY[...];空值生成恒假条件 1 = 2;值中的单引号会被转义防注入
  • 无条件查询全表:用 FilterBuilder.Empty,禁止向需要过滤条件的方法传 null

下一步​

相关实现​

  • 模型与枚举:chameleon-core/components/Chameleon.Common/Models/DynamicFilter.cs
  • 工厂类:chameleon-core/components/Chameleon.Database/FilterBuilder.cs
  • 转 FreeSql 条件:chameleon-core/components/Chameleon.Database/DynamicFilterExtension.cs(ToDynamicFilterInfo / Normalize)
  • Act 自定义过滤器:chameleon-core/components/Chameleon.Database/CustomDynamicFilters/ArraryContainsFilterImpl.cs