如何将EF Core自定义映射PostgreSQL原生函数转换为?
摘要:背景 在 ASP.NET Core 应用开发中,使用 PostgreSQL的jsonb类型存储多语言数据是一种常见的方案。这种方式相比传统的多列存储或独立翻译表,在模式定义上更加灵活。 例如,对于一个包含多语言简介的“艺术家”实体,我们通常
背景
在 ASP.NET Core 应用开发中,使用 PostgreSQL的jsonb类型存储多语言数据是一种常见的方案。这种方式相比传统的多列存储或独立翻译表,在模式定义上更加灵活。
例如,对于一个包含多语言简介的“艺术家”实体,我们通常如下定义:
public class ArtistEntity
{
public int Id { get; set; }
// 使用 jsonb 存储多语言字典:Key=语言代码(en, zh-CN), Value=内容
[Column(TypeName = "jsonb")]
public Dictionary<string, string> Biography { get; set; } = [];
}性能考量
虽然存储方便,但在读取时会面临流量和性能问题。在大多数业务场景中,前端仅需要展示当前用户语言(如英语)的内容。如果直接查询实体,EF Core会将包含所有语言的JSONB对象完整加载到内存中。对于包含几十种语言的长文本字段,这不仅浪费数据库 I/O,也增加了网络传输开销。
尝试使用EF Core的字典索引器语法:
// 期望生成的 SQL 是直接取值
var bio = context.Artists.Select(x => x.Biography["en"]).FirstOrDefault();根据 Npgsql EF Core Provider 文档,虽然 Provider 提供了如EF.Functions.JsonContains、EF.Functions.JsonExists等丰富的 JSONB 操作函数,但在处理 Dictionary 索引器的投影翻译时仍存在局限性。在某些复杂的 Select 投影中,它可能无法生成最优的 ->> 操作符,或者导致查询在客户端求值。
目前Npgsql的EF.Functions中并没有直接对应jsonb_extract_path_text的方法,而这个原生函数恰恰是解决此类需求最直接的方式。它能在数据库服务端完成解析,仅返回指定路径的文本值。
什么是 jsonb_extract_path_text?
jsonb_extract_path_text是PostgreSQL的原生函数(等同于操作符 #>>),专门用于从 JSON 数据中根据路径提取文本。
相比于直接返回 JSON 对象,它能直接返回纯文本(text 类型),非常适合提取多语言字典中的单一语言值。
假设数据库里的 Biography 字段存储如下 JSON:
{
"en": "Hello World",
"zh-CN": "你好世界",
"fr": "Bonjour le monde"
}
如果我们只想获取中文简介:
-- 使用函数提取 'zh-CN' 键的值
SELECT jsonb_extract_path_text("Biography", 'zh-CN')
FROM "Artists";
-- 结果仅返回字符串: "你好世界"
这种处理方式完全在数据库端完成,传输到应用层的只有这4个字符,而不是包含英文和法文的完整JSON 对象。
解决方案:映射自定义函数
为了在EF Core中使用jsonb_extract_path_text,我们可以通过自定义函数映射来实现。
什么是 EF Core自定义函数?
EF Core 的自定义函数映射(User-defined function mapping)允许开发者 C#方法直接映射到数据库中的 SQL 函数。在 LINQ 查询中使用这些被映射的 C# 方法时,EF Core不会在客户端执行它们,而是将它们“翻译”成对应的 SQL 片段发送给数据库执行。这就像是给了你一把钥匙,让你能够从 C# 代码中直接调用数据库特有的、强大的原生能力(如 PostgreSQL 的 JSON 处理、GIS 地理信息计算等),而无需编写原生的 SQL 字符串。
1. 定义函数存根
在 C# 中定义一个静态方法作为存根(Stub),用于告诉 EF Core 即使翻译 SQL。
