如何查询 Npgsql.EntityFrameworkCore 中具有 JSON 数组的列

use*_*870 8 c# postgresql npgsql entity-framework-core jsonb

笔记:

  • 使用 Npsql.EntityFrameworkCore.PostgreSQL v3.1.4
  • 使用 Npgsql v4.1.3.1
  • 使用代码优先方法

我有下表(称为汽车): 在此输入图像描述

它有两列:

  • LicenseNumber(输入文本)(在 Car.cs 模型中输入字符串)
  • KitchenIntegrations(jsonb 类型)(Car.cs 中的列表类型)其中 Integrations 是集成类型的列表。

Car 类如下所示:

public class Car
{
   public string LicenseNumber {get;set;}

   public List<Kitchen> KitchenIntegrations {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

Kitchen.cs 看起来像这样:

public class Kitchen
{
   public int Id {get;set;}

   public string KitchenName {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

最后我的 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);
                }
            );
        }
    }
Run Code Online (Sandbox Code Playgroud)

Cars表中,我只需要获取 Id = 1 的 KitchenIntegration。

我可以在 PSQL 中轻松完成此操作,但在尝试查询 JSON 数组时遇到问题。

我试过:

var integrations = context.Cars.Select(i => i.KitchenIntegrations.First(o => o.Id == 1)).ToList();
Run Code Online (Sandbox Code Playgroud)

但遇到一个问题,无法将其转换为 SQL/PSQL。

所以我的问题是如何遍历 EntityFrameworkCore 中的 JSON 数组或列表?(如果可能的话,只在服务器端而不是客户端进行)。

谢谢你!任何帮助是极大的赞赏!

Бон*_*ков 15

我的 2 美分。假设您有一个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"}]
Run Code Online (Sandbox Code Playgroud)

然后使用EF Core 3.1andNpgsql.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));
Run Code Online (Sandbox Code Playgroud)

  • 到目前为止,这对我来说效果很好。谢谢 (2认同)

Sha*_*sky 0

(当前)不支持将其转换为 SQL - 对数据库 JSON 列的操作受到限制,请参阅文档以获取支持的转换列表。

在这种特殊情况下,尚不清楚如何准确地将其(有效)转换为 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();
Run Code Online (Sandbox Code Playgroud)

这确实有一个缺点,就是下载大量不需要的厨房实例,然后在客户端将它们过滤掉。如果从性能角度来看这是有问题的,请考虑使用原始 SQL(尽管同样,您想要做的事情并不简单)。