EF Core 6中OPENJSON自定义翻译:传递列名而非字符串的实现
解决方案
方案1:注册表值函数+EF.Property获取原始列
EF Core 6支持注册SQL内置的表值函数,结合EF.Property可以直接映射到数据库列,避免参数被转成字符串字面量。
- 定义表值函数的静态声明
创建一个静态类,声明对应SQLOPENJSON的方法,标记为EF内置函数:
public static class JsonDbFunctions { // 映射到SQL的OPENJSON表值函数 [DbFunction("OPENJSON", IsBuiltIn = true)] public static IEnumerable<JsonElement> OpenJson(string jsonColumn) { // 仅用于EF翻译,运行时不会执行 throw new NotImplementedException("仅支持EF Core查询翻译场景"); } }
- 在DbContext中注册函数
在OnModelCreating方法里注册这个表值函数,让EF识别它:
protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); var openJsonMethod = typeof(JsonDbFunctions).GetMethod(nameof(JsonDbFunctions.OpenJson)); modelBuilder.HasDbFunction(openJsonMethod) .HasName("OPENJSON") .IsBuiltIn(); }
- 编写查询逻辑
使用EF.Property<string>(item, nameof(Item.Sections))获取Sections列的原始字符串值(绕开EF对IEnumerable<Section>的映射),传给自定义的OpenJson方法,再结合JSON_VALUE做过滤:
var targetValue = "需要匹配的内容"; var query = dbContext.Items .Where(item => JsonDbFunctions.OpenJson(EF.Property<string>(item, nameof(Item.Sections))) .Any(jsonSection => EF.Functions.JsonValue(jsonSection.GetRawText(), "$.你的属性名") == targetValue)); var result = await query.ToListAsync();
这段代码会生成正确的SQL:
SELECT [i].[Id], [i].[Sections], ... FROM [Items] AS [i] WHERE EXISTS ( SELECT 1 FROM OPENJSON([i].[Sections]) AS [j] WHERE JSON_VALUE([j].[value], N'$.你的属性名') = N'需要匹配的内容' )
方案2:直接使用FromSqlRaw(快速临时方案)
如果不想修改DbContext配置,可以直接用原生SQL查询,再映射回实体:
var targetValue = "需要匹配的内容"; var result = await dbContext.Items .FromSqlRaw(@" SELECT * FROM Items WHERE EXISTS ( SELECT 1 FROM OPENJSON(Sections) j WHERE JSON_VALUE(j.value, '$.你的属性名') = {0} )", targetValue) .ToListAsync();
关键说明
EF.Property<T>是核心:它告诉EF直接访问实体对应的数据库列,而非映射后的IEnumerable<Section>属性,因此不会生成字符串字面量'Sections'。- 表值函数注册:EF Core 6对内置表值函数的翻译支持完善,注册后可以无缝结合LINQ查询使用。
内容的提问来源于stack exchange,提问作者vgru
相关产品推荐
相关产品推荐

