如何查询在 Npgsql.EntityFrameworkCore 中具有 JSON 数组的列
How to query against a column that has a JSON Array in Npgsql.EntityFrameworkCore
备注:
- 使用 Npsql.EntityFrameworkCore.PostgreSQL v3.1.4
- 使用 Npgsql v4.1.3.1
- 使用代码优先方法
我有以下 table(称为 Cars):
它有两列:
- LicenseNumber(键入文本)(在 Car.cs 模型中键入字符串)
- KitchenIntegrations(类型 jsonb)(在 Car.cs 中类型列表)
其中 Integrations 是集成类型列表。
汽车 class 看起来是这样的:
public class Car
{
public string LicenseNumber {get;set;}
public List<Kitchen> KitchenIntegrations {get;set;}
}
Kitchen.cs 看起来像这样:
public class Kitchen
{
public int Id {get;set;}
public string KitchenName {get;set;}
}
最后我的 CarContext.cs 看起来像这样:
public class CarContext : DbContext
{
public DbSet<Car> Cars { get; set; }
public CarContext()
{
}
public CarContext(DbContextOptions<CarContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseNpgsql("ConnectionStringGoesHere");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("public");
modelBuilder.Entity<Car>(
builder =>
{
builder.HasKey(i => i.LicenseNumber);
builder.Property(i => i.KitchenIntegrations).HasColumnType("jsonb").IsRequired(false);
}
);
}
}
在 Cars table 中,我只需要获取 ID = 1 的 KitchenIntegration。
我可以在 PSQL 中轻松做到这一点,但我在尝试查询 JSON 数组时遇到问题。
我试过了:
var integrations = context.Cars.Select(i => i.KitchenIntegrations.First(o => o.Id == 1)).ToList();
但是遇到无法翻译成 SQL/PSQL 的问题。
所以我的问题是如何在 EntityFrameworkCore 中遍历 JSON 的数组或列表? (如果可能的话,只做服务器端而不是客户端)。
谢谢!非常感谢任何帮助!
我认为这是因为 Entity Framework 核心中的 Lazy Loading。
这可以用 var integrations = context.Cars.Select(c => c.KitchenIntegrations).Select(l => l.First(o => o.Id == 1)).ToList();
来验证。它会抛出:
When called from 'VisitLambda', rewriting a node of type 'System.Linq.Expressions.ParameterExpression' must return a non-null value of the same type. Alternatively, override 'VisitLambda' and change it to not visit children of this type.
但是,我尝试 Eager Loading 和 var integrations = context.Cars.Include(c => c.KitchenIntegrations).ToList();
(以及 ThenInclude
),但它抛出错误:
Lambda expression used inside Include is not valid.
我必须将所有 Cars
预加载到内存中才能 使其工作:
var integrations = context.Cars
.ToList() // Eager load to memory
.Select(i => i.KitchenIntegrations.First(o => o.Id == 1))
.ToList();
我觉得应该有更好(或正确)的方式来进行预先加载。
(当前)不支持将此翻译成 SQL - 对数据库 JSON 列的操作是有限的,请参阅 the docs 以获取支持的翻译列表。
在这种特殊情况下,尚不清楚如何(有效地)将其准确地转换为 SQL。有关类似问题,请参阅 https://github.com/npgsql/efcore.pg/issues/1534。
您确实可以按照@han-zhao的建议在客户端执行投影。但是,使用 AsEnumerable 来触发客户端评估而不是 ToList:
var integrations = context.Cars
.AsEnumerable()
.Select(i => i.KitchenIntegrations.First(o => o.Id == 1))
.ToList();
这确实有下载很多不需要的厨房实例的缺点,只能在客户端过滤掉它们。如果 perf-wise 有问题,请考虑使用 raw SQL(尽管同样,您想要做的事情并非微不足道)。
我的 2 美分。
假设您有一个具有以下结构的 table fixtures
。
(Id, JsonProperty)
。
假设您在数据库中有一条这样的记录。
1, [{"Name": "Test", "Value": "123"}, {"Name": "Test2", "Value": "pesho"}]
2, [{"Name": "Test", "Value": "321"}, {"Name": "Test2", "Value": "pesho"}]
3, [{"Name": "Test", "Value": "1123"}, {"Name": "Test2", "Value": "pesho"}]
然后使用 EF Core 3.1
和 Npgsql.EntityFrameworkCore.PostgreSQ 3.14
你可以这样做:
var search = "[{\"Value\": \"123\"}]";
var result = dbContext.Fixtures
.FirstOrDefault(s => EF.Functions.JsonContains(s.JsonProperty, search));
var search2 = "[{\"Name\": \"Test\"}]";
var multipleResults = dbContext.Fixtures
.Where(s => EF.Functions.JsonContains(s.JsonProperty, search2));
备注:
- 使用 Npsql.EntityFrameworkCore.PostgreSQL v3.1.4
- 使用 Npgsql v4.1.3.1
- 使用代码优先方法
我有以下 table(称为 Cars):
它有两列:
- LicenseNumber(键入文本)(在 Car.cs 模型中键入字符串)
- KitchenIntegrations(类型 jsonb)(在 Car.cs 中类型列表) 其中 Integrations 是集成类型列表。
汽车 class 看起来是这样的:
public class Car
{
public string LicenseNumber {get;set;}
public List<Kitchen> KitchenIntegrations {get;set;}
}
Kitchen.cs 看起来像这样:
public class Kitchen
{
public int Id {get;set;}
public string KitchenName {get;set;}
}
最后我的 CarContext.cs 看起来像这样:
public class CarContext : DbContext
{
public DbSet<Car> Cars { get; set; }
public CarContext()
{
}
public CarContext(DbContextOptions<CarContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseNpgsql("ConnectionStringGoesHere");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("public");
modelBuilder.Entity<Car>(
builder =>
{
builder.HasKey(i => i.LicenseNumber);
builder.Property(i => i.KitchenIntegrations).HasColumnType("jsonb").IsRequired(false);
}
);
}
}
在 Cars table 中,我只需要获取 ID = 1 的 KitchenIntegration。
我可以在 PSQL 中轻松做到这一点,但我在尝试查询 JSON 数组时遇到问题。
我试过了:
var integrations = context.Cars.Select(i => i.KitchenIntegrations.First(o => o.Id == 1)).ToList();
但是遇到无法翻译成 SQL/PSQL 的问题。
所以我的问题是如何在 EntityFrameworkCore 中遍历 JSON 的数组或列表? (如果可能的话,只做服务器端而不是客户端)。
谢谢!非常感谢任何帮助!
我认为这是因为 Entity Framework 核心中的 Lazy Loading。
这可以用 var integrations = context.Cars.Select(c => c.KitchenIntegrations).Select(l => l.First(o => o.Id == 1)).ToList();
来验证。它会抛出:
When called from 'VisitLambda', rewriting a node of type 'System.Linq.Expressions.ParameterExpression' must return a non-null value of the same type. Alternatively, override 'VisitLambda' and change it to not visit children of this type.
但是,我尝试 Eager Loading 和 var integrations = context.Cars.Include(c => c.KitchenIntegrations).ToList();
(以及 ThenInclude
),但它抛出错误:
Lambda expression used inside Include is not valid.
我必须将所有 Cars
预加载到内存中才能 使其工作:
var integrations = context.Cars
.ToList() // Eager load to memory
.Select(i => i.KitchenIntegrations.First(o => o.Id == 1))
.ToList();
我觉得应该有更好(或正确)的方式来进行预先加载。
(当前)不支持将此翻译成 SQL - 对数据库 JSON 列的操作是有限的,请参阅 the docs 以获取支持的翻译列表。
在这种特殊情况下,尚不清楚如何(有效地)将其准确地转换为 SQL。有关类似问题,请参阅 https://github.com/npgsql/efcore.pg/issues/1534。
您确实可以按照@han-zhao的建议在客户端执行投影。但是,使用 AsEnumerable 来触发客户端评估而不是 ToList:
var integrations = context.Cars
.AsEnumerable()
.Select(i => i.KitchenIntegrations.First(o => o.Id == 1))
.ToList();
这确实有下载很多不需要的厨房实例的缺点,只能在客户端过滤掉它们。如果 perf-wise 有问题,请考虑使用 raw SQL(尽管同样,您想要做的事情并非微不足道)。
我的 2 美分。
假设您有一个具有以下结构的 table fixtures
。
(Id, JsonProperty)
。
假设您在数据库中有一条这样的记录。
1, [{"Name": "Test", "Value": "123"}, {"Name": "Test2", "Value": "pesho"}]
2, [{"Name": "Test", "Value": "321"}, {"Name": "Test2", "Value": "pesho"}]
3, [{"Name": "Test", "Value": "1123"}, {"Name": "Test2", "Value": "pesho"}]
然后使用 EF Core 3.1
和 Npgsql.EntityFrameworkCore.PostgreSQ 3.14
你可以这样做:
var search = "[{\"Value\": \"123\"}]";
var result = dbContext.Fixtures
.FirstOrDefault(s => EF.Functions.JsonContains(s.JsonProperty, search));
var search2 = "[{\"Name\": \"Test\"}]";
var multipleResults = dbContext.Fixtures
.Where(s => EF.Functions.JsonContains(s.JsonProperty, search2));