前言

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. linux mysql 允许进行远程连接 比如 navicat

    出于安全方面考虑默认只允许本机(localhost, 127.0.0.1)来连接访问.所以开启远程访问权限.登录mysqlmysql -uroot -pxxxxxx 1:GRANT ALL PRIVI ...

  2. Python 基于win32com客户端实现Excel操作

    测试环境 Python 3.6.2 代码实现 非多线程场景下使用 新建并保存EXCEL import win32com.client from win32api import RGB def save ...

  3. Django 自定义装饰器解决MySQL server has gone away错误

    Django 自定义装饰器解决MySQL server has gone away错误 by:授客 QQ:1033553122 测试环境 Win 10   Python 3.5.4   Django- ...

  4. appium模拟键盘事件

    原方法: 1 def press_keycode(self, keycode, metastate=None): 2 """Sends a keycode to the ...

  5. python中pip安装包出现Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None))…………或无法pip install packname安装依赖包

    问题: 安装包出现Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connec ...

  6. 加速 Mac 时间机器备份

    加速 Mac 时间机器备份速度教程,Time Machine 备份太慢的解决方法 @Pertim 2020-09-11 相信用过一段时间电脑的人,都知道经常备份 macOS 系统的重要性了.特别是最近 ...

  7. RHCA rh442 006 中断号 缓存命中率 内存概念 大页

    IRQ均衡 硬中断 IRQ是中断号 2003 电脑 拨号 56K Modem USB 打印机 拨号成功,打印机会是乱码,他们会不兼容 因为终端号一样 (类似ip地址冲突) 在bios里面调整设备的中断 ...

  8. Jmeter函数助手8-counter

    counter函数用于线程计数,类似计数器. TRUE每个用户有自己的计数器:FALSE使用全局计数器:即线程之间是否需要共享累加计数器,TRUE否,FALSE是 存储结果的变量名(可选) 1.线程之 ...

  9. 【SQL】 去掉最后一段,只保留前段

    需求描述: 例如给出这样一个地址或者其他字符: 10.11.12.13 192.168.177.209101.102.103.104.105 ... 要求只保留前面的部分,去掉最后一部分 10.11. ...

  10. 美国小伙: "American Guy: Only communism can save America!"

    视频地址: https://www.youtube.com/watch?v=Y_WQnXFh8ss 2024大选在即,又是拜登对阵特朗普的旧日重现.在角逐谁的对手反对者更多的畸形内耗中,有一个名为 M ...