如何像使用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
相关产品推荐
相关产品推荐

