访问Jsonb字段内的Npgsql属性

PostgreSQL具有Jsonb数据类型,它允许您向标准关系模型中添加附加属性,并能够搜索它们。


具有Npgsql扩展名的EntityFramework Core可以将字段数据拉到System.String类型


但是,要在查询级别通过EF过滤Json属性,您必须使用纯SQL,这不是很方便,因为您需要进入映射(如果不是自动的),请查找与模型属性相对应的字段名称,以支持此命名。 ORM给我们带来的灵活性已丧失。


如果它使您和我都感到沮丧,欢迎猫来。


在文章的末尾有一个到源的链接!


表示任务


作为一名开发人员,我想要一个工具来访问Jsonb字段,目的是按它们进行过滤和排序,它是:


  • 它将与EntityFramework Core 2兼容(我们使用它)
  • 使用它不需要您自己编写SQL
  • 将与平面Json结构一起使用(在json内只有json属性)

添加Npgsql.Json.NET ,它可以将Json和Jsonb值投影到CLR类型中。 老实说,我不明白这可能是什么意思,因为由于我们在关系数据库中需要一个Json字段,因此很可能我们拥有具有动态字段集的实体。


解决问题的算法


  1. 定义可以满足我们需求的一种或多种方法。
  2. 创建一个将参与SQL代码生成的翻译器。
  3. 全部拧到Npgsql。

解决方案


首先,我们定义一个方法。 我希望它被这样使用:


 context.Entity.Where(x => JsonbMethods.Value<string>(x.JsonbField, "jsonPropertyName") == "value") 

因此,这是我们的方法:


 public static TSource Value<TSource>(object jsonbProperty, string jsonbPropertyName) { throw new NotSupportedException(); } 

几个小时以来,我一直在选择EF Core,Npgsql的资源,而不仅仅是在寻找扩展SQL生成的基本功能的方法。 我看了这篇文章 ,但是我不喜欢作者使用的连接翻译器方法的方法,因为它重新定义了标准工具,这意味着它可能与另一个类似的工具发生冲突。
结果,我找到了Net Topology Suite的源代码。 我所需要的只是一种方法连接方法转换器。


但是大多数时间我都花在生成所需的sql片段上。


这是必需的语法


tableAlias."JsonField"->>"insideProperty"


最初,我尝试在翻译器中返回ColumnExpression。 创建它时,第一个参数是列名(字符串)。 我只是根据方法中提供的参数来烹饪它。 启动,检查,错误。 事实证明,我通过的名字用引号引起来。 结果,SQL原来是tableAlias.""JsonField"->>"insideProperty""


在生成器的源代码中,我发现了VisitColumn方法,该行为是硬编码的,并且不依赖于任何参数。 也就是说,我不能影响它。 有必要寻找另一种解决方案。


然后,我创建了自己的Expression - JsonbPropertyAccessorExpression: Expression


仍然需要重写其ISqlExpressionVisitor Accept方法。


但麻烦的是,在此接口中没有自定义运算符可以分段的方法。 然后,我想到访问的不是一种方法,而是几种。 首先访问VisitColumn ,它创建对tableAlias。“ JsonField”列的访问,然后VisitSqlFragment ,在其中添加了"->>'insideFieldName'"


我没有希望,但是成功了。 差不多了


当我出于某种原因而尝试按文本进行过滤时,由于某种原因, tableAlias."JsonField"->>"insideProperty" = JSONB "value"这样一个tableAlias."JsonField"->>"insideProperty" = JSONB "value"过滤器: tableAlias."JsonField"->>"insideProperty" = JSONB "value" ,这导致了错误,因为如果文本不包含有效的Json,则无法将其转换为JSONB类型。 为何在我需要文字时需要引导某事?


我什至决定从映射模型的Jsonb列中删除标记,即Jsonb,仅将此标记添加到MigrationContext以便生成正确的迁移。 它甚至起飞了,但是在我看来,这种方法似乎很困难。 不过,我停在那里。


之后,我将设置为CAST,因为Value方法是通用的,并且Json属性中可以有不同类型的数据,这些数据也需要排序和过滤。


结果,我开始从我的翻译器返回ExplicitCastExpression ,在其中传递了我自定义的ExpressionValue方法的通用参数中包含的类型。


当我按日期搜索结果SQL时,我发现比较的值被强制转换为时间戳类型。 timestamp 'some date value' 。 然后它突然降临在我身上。 我用拐杖解决了以前的问题,它自己消失了。 当将访问器转换为Json字段的文本时,生成器不再向JSONB添加显式转换,因为比较操作已经在左侧包含文本,并且默认情况下,Jsonb字段的访问器返回Jsonb类型。


最后


最后,我想补充一点,我没有找到有关如何添加属性和方法的自定义转换器的文档。 可能看起来很糟糕。 如果有人对方法,代码等有评论,请在评论中写下。


如果有人想分叉扩展库,请写一封私人信件,我将尽力提供帮助。 好吧,还是抛出pullrequests。


这是到源的链接

Source: https://habr.com/ru/post/zh-CN463747/


All Articles