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

如何像使用IQueryable一样操作Action<IBoundClient>表达式

问题

我有一个通过OData与其他服务通信的服务,项目使用NuGet包Simple.OData.Client,且必须使用公司提供的通信接口方法FetchEntitiesAsync,其代码如下:

private async Task<IEnumerable<T>> FetchEntitiesAsync<T>(string prefix, Action<IBoundClient<T>> configure,
    CancellationToken token) where T : class
{
    IBoundClient<T> query = _client.For<T>(prefix);
    configure(query);

    try
    {
        this.Logger.LogDebug($"Fetching {_baseUri}/{prefix} via OData...");

        IEnumerable<T> entities = await query.FindEntriesAsync(token);
        return entities;
    }
    catch (Exception e)
    {
        this.Logger.LogError(e, $"{prefix} acquisition OData exception");
        this.Logger.LogError(e.StackTrace);

        throw e;
    }
}

我需要传入一个Action<IBoundClient<T>>参数,目前可以写出简单的表达式:

Action<IBoundClient<MyClass>> filter = (q => q.Filter(i => i.Property1 == myProperty1).Expand(i => i.MyParameters));

FetchEntitiesAsync("MyClass", filter, token);

但当查询逻辑复杂时,只能通过分支语句定义不同的Action,代码会变得十分混乱:

Action<IBoundClient<MyClass>> filter;

if (string.IsNullOrEmpty(property2))
{
    filter = (q => q.Filter(i => i.Property1 == myProperty1)
           .Expand(i => i.Columns));
}
else
{
    filter = (q => q.Filter(i => i.Property1 == myProperty2)
           .Filter(i => i.Property2.ToLower() == property2.ToLower())
           .Expand(i => i.Columns));
}

FetchEntitiesAsync("MyClass", filter, token);

我希望能像在EntityFramework中操作IQueryable那样,逐步构建查询逻辑,示例如下:

myQuery = myQuery.Where(x=> x.Property1 = myProperty1);

if(!string.IsNullOrEmpty(property2)){
  myQuery = myQuery.Where(x=> x.Property2.ToLower() = property2.ToLower());
}

[...]

请问该如何实现这种灵活的查询构建方式?

解决方案

你可以通过两种方式实现类似EF的分步查询构建,完全兼容现有FetchEntitiesAsync接口:

方法1:直接在Action委托内分步编写逻辑

不需要额外封装,把动态分支逻辑直接写在传入的Action内部,风格和操作IQueryable一致:

await FetchEntitiesAsync("MyClass", q => 
{
    // 基础查询条件
    q.Filter(i => i.Property1 == myProperty1);
    
    // 动态添加过滤条件
    if (!string.IsNullOrEmpty(property2))
    {
        q.Filter(i => i.Property2.ToLower() == property2.ToLower());
    }
    
    // 固定的关联展开
    q.Expand(i => i.Columns);
}, token);

Simple.OData.Client的IBoundClient<T>支持多次调用Filter方法,底层会自动将多个条件组合成AND逻辑,完全满足分步拼接需求。

方法2:封装查询构建器(复杂场景推荐)

如果查询逻辑涉及大量复用片段或多维度条件组合,可以封装一个构建器类,把查询逻辑拆分成可链式调用的方法:

public class ODataQueryBuilder<T> where T : class
{
    private readonly List<Action<IBoundClient<T>>> _configActions = new();

    public ODataQueryBuilder<T> Filter(Expression<Func<T, bool>> filterExpr)
    {
        _configActions.Add(q => q.Filter(filterExpr));
        return this;
    }

    public ODataQueryBuilder<T> Expand(Expression<Func<T, object>> expandExpr)
    {
        _configActions.Add(q => q.Expand(expandExpr));
        return this;
    }

    // 可扩展添加OrderBy、Select等其他OData操作方法
    public ODataQueryBuilder<T> OrderBy(Expression<Func<T, object>> orderExpr)
    {
        _configActions.Add(q => q.OrderBy(orderExpr));
        return this;
    }

    public Action<IBoundClient<T>> Build()
    {
        return q => 
        {
            foreach (var action in _configActions)
            {
                action(q);
            }
        };
    }
}

使用方式如下:

var queryBuilder = new ODataQueryBuilder<MyClass>()
    .Filter(i => i.Property1 == myProperty1)
    .Expand(i => i.Columns);

// 根据条件动态追加查询规则
if (!string.IsNullOrEmpty(property2))
{
    queryBuilder.Filter(i => i.Property2.ToLower() == property2.ToLower());
}
if (sortByCreateTime)
{
    queryBuilder.OrderBy(i => i.CreateTime);
}

// 传入构建好的Action
await FetchEntitiesAsync("MyClass", queryBuilder.Build(), token);

这种方式让复杂查询逻辑更清晰,也方便复用不同的查询片段。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 07:24:19