前言

EF Core 可以把 expression 转换成 string, 但没办法转回来.

想把 string 转成 expression, 目前最合适的工具是 OData. 虽然 Dynamic LINQ 也有人用, 但毕竟 OData 是微软的, 而且有规范文档.

可惜, 就目前的 OData 要想做到 string to expression 还是很麻烦的. 这也是 OData.NxT 想达到的其中一个目标. 但以 OData Team 的实力, 估计还要等好多年呢. 我们还是实际一点, 来看看目前该怎么弄吧.

主要参考:

OData Nxt 004: Query to Expression (IQueryable)

Using Uri Parser

分析源码之路

如果你只是想看结果, 可以跳过这个 part, 如果想学习怎样通过源码找方案可以看看.

OData 肯定是有办法做到的, 在 ODataController 里, 可以通过 ODataQueryOptions.ApplyTo 来把 query params apply 进去 queryable.

猜测它内部就是把 string 转成 expression 然后调用 queryable.where(expression), 大概是这样.

但我们要怎样从 0 搞起呢? 怎么弄 OdataQueryOptions 出来呢?

接着查下去.

打开 ODataQueryOptions.cs

发现要初始化它就需要有一个 HttpRequest...这个接口太上层了吧, 和 HttpRequest 绑的这么死...

接着查下去

在 ApplyTo 方法里看到了 handle $filter 的 apply. 我这里只 focus $filter 的处理 (因为我这一次也只需要到这个).

底层调用的是 FilterQueryOption.ApplyTo

FilterQueryOption.cs

初始化它需要 content 和 parser. 嗯...感觉这个接口就比较底层了, 是我们要的了. 先不管 context, parser 怎么搞, 我们去看看它的 apply 做些什么.

ApplyTo

和我们猜测的一样, 里面搞了一个 queryable.where(expression). 它是利用了 FilterBinder.Bind 方法来把 string 转成 expression.

如果你有看上面的参考视频, 这个就是作者通过反射调用的方法. 因为这个 Bind 是 internal 方法来的, 所以他要反射.

FilterBinder.cs

好, 既然确定找对了路, 那么我们回去看看怎样从 0 做出来, context 和 parser 怎样弄.

ODataQueryContext.cs

初始化需要 EdmModel, ClrType, ODataPath.

EdmModel 就是我们每次 startup.cs 要搞的, 它和 EF Core 的 model 是同一个概念.

var odataBuilder = new ODataConventionModelBuilder();
odataBuilder.EntitySet<Product>("products");
odataBuilder.EnableLowerCamelCase();
var edmModel = odataBuilder.GetEdmModel();

ClrType 指的就是这一次的 $filter 的起头. OData 是对着 resource (entity) 的, 一定会有一个开始的 entity.

上面例子就是 typeof(Product).

ODataPath 我们参考它怎么做.

EnableQueryAttribute.cs

在 request 阶段就已经生产的, 全场找一下后它是这样做的

var entitySet = edmModel.FindDeclaredEntitySet("products");
var entitySetSegment = new EntitySetSegment(entitySet);
var odataPath = new ODataPath(new[] { entitySetSegment });

context 搞定, 下一个要做的是 parser.

ODataQueryOptionParser.cs (源码是在 odata.net library)

初始化有几个重载, 先挑一个最简单实现的呗.

model 有了, odataPath 有了,

queryOptions

Using Uri Parser 学的, 但这里只教到怎样 parser 出 filter clause 但是没有教怎样转成 expression.

搞定. 到这里我们就掌握了所有的资料了.

写 Demo

var odataBuilder = new ODataConventionModelBuilder();
odataBuilder.EntitySet<Product>("products");
odataBuilder.EnableLowerCamelCase();
var edmModel = odataBuilder.GetEdmModel(); // 做 edm model
var entitySet = edmModel.FindDeclaredEntitySet("products");
var entitySetSegment = new EntitySetSegment(entitySet);
var odataPath = new ODataPath(new[] { entitySetSegment }); // 做 odata path
var queryOptions = new Dictionary<string, string> // 定义 $filter query
{
["$filter"] = "id eq 2",
};
var queryOptionParser = new ODataQueryOptionParser(edmModel, odataPath, queryOptions); // 做 parser
var context = new ODataQueryContext(edmModel, typeof(Product), odataPath); // 做 context
var filterQueryOption = new FilterQueryOption("id eq 2", context, queryOptionParser); // 做 filter options
var productQuery = new List<Product> { new Product { Id = 1 }, new Product { Id = 2 } }.AsQueryable(); // 定义 queryable
var newQuery = filterQueryOption.ApplyTo(productQuery, new ODataQuerySettings()); // apply to
var result = newQuery.OfType<Product>().ToList(); // 得到结果

这样就实现了一个通过 OData 把 query string apply to queryable 的过程了。

除了 FilterQueryOption,其它的 OrderByQueryOption、SkipQueryOption、TopQueryOption 用法一模一样,只要拿 newQuery 继续 ApplyTo 就可以了。

注:FilterQueryOption("rawValue") 这个 rawValue 挺奇葩的,queryOptions 都已经表达了,为什么这里需要重复呢?

我测试了一下,其实只要不是放 null or empty string,你放任何值都可以通过,而且效果都是正确的。看源码好像这个 rawValue 是给其它 internal overload constructor 用来生成 queryOptions,估计是代码没写好呗。

如果我们不想 apply 只想获取到 expression 行不行?

不行, 除非像参考视频里那样, 通过反射去调用 internal 的 Bind 方法. 然后做 apply to 所有的动作, 除了最后一个 where.

总结

OData 确实很不错, 但想把 query string 转换成 expression 目前还没有一个 build-in 的 solution.

也有一些 library 可以做到类似的效果

Is there a ODATA query to linq where expression (ODATA to Linq )

本篇尝试探索如果利用 OData 现有的接口来尽量做到 query to expression. 同时期待 OData.Neo 带给开发者们更好的使用体验.

 

OData – Query to Expression的更多相关文章

  1. [转]Supporting OData Query Options in ASP.NET Web API 2

    本文转自:https://docs.microsoft.com/en-us/aspnet/web-api/overview/odata-support-in-aspnet-web-api/suppor ...

  2. Dynamics CRM2015 2015版本可用的OData Query Designer工具

    2015后很多工具无法使用,包括2011版的OData Query Designer,这里介绍一款可用的工具,Dynamics XRM Tools for CRM 2015,下载地址:https:// ...

  3. Dynamic CRM 2015学习笔记(5)CRM 2015 导入 OData Query Designer 解决方案

    以前一直使用OData Query Designer来生成.验证odata查询字符串,本想把它导入到CRM 2015的环境里,但报错: 到MSDN上发现太老版本的solution确实不能再导入到crm ...

  4. [转]Calling an OData Service From a .NET Client (C#)

    本文转自:https://docs.microsoft.com/en-us/aspnet/web-api/overview/odata-support-in-aspnet-web-api/odata- ...

  5. [转]Upgrading to Async with Entity Framework, MVC, OData AsyncEntitySetController, Kendo UI, Glimpse & Generic Unit of Work Repository Framework v2.0

    本文转自:http://www.tuicool.com/articles/BBVr6z Thanks to everyone for allowing us to give back to the . ...

  6. [转]Using OData from ASP.NET

    本文转自:http://www.drdobbs.com/windows/using-odata-from-aspnet/240168672 By Gastón Hillar, July 01, 201 ...

  7. ODATA WEB API(一)---扩展使用

    一.概述 时间也算充足,抽点时间总结下OData的常用的使用方式,开放数据协议(OData)是一个查询和更新数据的Web协议.OData应用了web技术如HTTP.Atom发布协议(AtomPub)和 ...

  8. 让Asp.Net WebAPI支持OData查询,排序,过滤。

    让Asp.Net WebAPI支持OData后,就能支持在url中直接输入排序,过滤条件了. 一.创建Asp.Net WebAPI项目: 二.使用NuGet安装Asp.Net WebAPI 2.2和O ...

  9. Linq之Expression进阶

    目录 写在前面 系列文章 表达式树解析 表达式树特性 编译表达树 总结 写在前面 让我们首先简单回顾一下上篇文章介绍的内容,上篇文章介绍了表达式树的基本概念(表达式树又称为“表达式目录树”,以数据形式 ...

  10. mvc api odata 查询选项之 $inlinecount ,$format 选项

    网上百度“odata 语法”会出来很多结果,其中有一项是比较一致的,那就是odata支持一下几种语法: $filter  条件表达式 -- 对应sql语句的where条件查询,如:/Categorie ...

随机推荐

  1. 使用ES6中Class实现手写PromiseA+,完美通过官方872条用例

    目录 Promise出现的原因 myPromise的实现要点 myPromise的实现 myPromise - 实现简单的同步 myPromise - 增加异步功能 myPromise - 链式调用( ...

  2. [oeasy]python0002_终端_CLI_GUI_编程环境_游戏_真实_元宇宙 🥊

    回忆   上次 了解了 python 语言的特点 历史悠久 功能强大 深受好评 已成趋势   3大主流操作系统 mac windows linux             添加图片注释,不超过 140 ...

  3. oeasy教您玩转vim - 22 - 配置文件

    配置文件 回忆上节课内容 我们上次了解到了状态横条 通过转义表示 item 控制 item 宽度的方法 将 item 成组的方法 还有一个总开关 laststatus 但是每次都要写很长的一段话来配置 ...

  4. ChatGPT的作用(附示例)

    ChatGPT介绍(内容由ChatGPT生成) ChatGPT是一款基于GPT(生成式预测网络)的聊天机器人,它可以根据用户输入自动生成相应的回复. GPT是由OpenAI开发的一种预测网络模型,其中 ...

  5. pytest数据驱动 pandas

    pytest数据驱动 pandas 主要过程:用pandas读取excel里面的数据,然后进行百度查询,并断言 pf = pd.read_excel('data_py.xlsx', usecols=[ ...

  6. 基于禅道数据库对bug进行不同维度统计

    工作中经常需要在周报.月报.年报对禅道bug数据进行不同维度统计导出,以下是我常用的统计sql 1.统计2022年每个月bug数(deleted='0'是查询未删除的bug) select DATE_ ...

  7. 【Java】Maven模块化工程SSM整合

    创建数据库一个演示表User CREATE TABLE `user` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(64) DEFAULT NU ...

  8. 国产操作系统——银河麒麟V10 SP1使用小结

    前几天看新闻国产操作系统银河麒麟有了新的更新于是上手搞了一个. ========================================================= 该系统自带驱动,其中 ...

  9. /usr/bin/ld cannot find -lGL

    安装mujoco报错: /usr/bin/ld cannot find -lGL 解决方法: sudo apt install libgl1-mesa-dev

  10. 这应该是全网最全的CSP-S初赛复习吧

    点我到洛谷看 \(Update\ 2024/8/2:\) 加入了在数据结构中增加了"树",做出部分更改. linux基础命令 cd 切换目录 ls 列出目前工作目录所含的文件及子目 ...