【问题标题】:Mapping to POCOs with top-level dictionary from Postgres jsonb column使用 Postgres jsonb 列中的顶级字典映射到 POCO
【发布时间】:2020-08-15 05:12:06
【问题描述】:

我有一个使用 EF Core 和 Postgres 数据库的 .NET Core 3.1 应用程序。在数据库中,我有一个 jsonb 列,现在我想将其映射到 EF Core 中定义明确的一组类。

jsonb 列的内容如下所示:

{
    "entry1": {
        "name": "entry1",
        "contents": {
            "entry1.1": {
                "name": "entry1.1"
            },
            "entry1.2": {
                "name": "entry1.2",
                "contents": {
                    "entry1.2.1": {
                        "name": "entry1.2.1"
                    }
                }
            }
        }
    }
}

在顶层,它是一个将字符串映射到条目的字典。每个条目都有一个名称并且可以有内容,这又是一个将字符串映射到条目的字典。

public class Entry
{
    public string name { get; set; }
    public Dictionary<string, Entry> contents { get; set; }
}

jsonb 列本身是在这样的表上定义的:

public class MyTable {
    [Column(TypeName = "jsonb")]
    public Dictionary<string, Entry> Entries { get; set; }
}

现在的问题是它根本不起作用。当我使用 EF Core 从数据库中获取条目时,“Entries”属性确实包含一个带有单个键“entry1”的字典,但该键的值是一个空的 Entry 对象(名称和内容均为空)。

Npgsql documentation on mapping jsonb columns to POCOs 没有解释在这种情况下如何处理字典。我在 jsonb 列中找不到任何带有顶级字典的示例,所以我不完全确定我这样做是否正确。

如何正确连接它,以便我的 jsonb 列映射到 Entry 对象的字典?

【问题讨论】:

  • 从我目前使用 Npgsql 的经验来看,映射似乎必须相当具体。这意味着您的 POCO 必须与您在 json 中提供的密钥相匹配。如果是我,我可能会将 JSON 作为一个完整的字符串读取,然后使用帮助程序/服务对其进行解析。看起来麻烦会出现在您的“入口”对象的级联中。
  • @GingerNinja 如果属性直接是列表或 Entry 类,它可以开箱即用,只有 Dictionary 不起作用。我不确定这是否只是 EF Core npgsql 没有实现的一种情况,或者我在这里做错了什么。刚才我发现了如何手动定义序列化,它甚至可以使用Entry对象之间的递归链接。但据我了解,这个版本的处事比较有限(例如不支持查询)

标签: .net-core entity-framework-core npgsql


【解决方案1】:

以下似乎效果很好:

class Program
{
    static void Main(string[] args)
    {
        using (var createCtx = new BlogContext())
        {
            createCtx.Database.EnsureDeleted();
            createCtx.Database.EnsureCreated();

            createCtx.Blogs.Add(new Blog
            {
                Entries = new Dictionary<string, Entry>
                {
                    { "bla", new Entry { Foo = "foo1" }}
                }
            });

            createCtx.SaveChanges();
        }

        using var ctx = new BlogContext();

        var results = ctx.Blogs.Single();
        Console.WriteLine(results.Entries["bla"].Foo);
    }
}

public class BlogContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    static ILoggerFactory ContextLoggerFactory
        => LoggerFactory.Create(b => b.AddConsole().AddFilter("", LogLevel.Information));

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseNpgsql(@"Host=localhost;Database=test;Username=npgsql_tests;Password=npgsql_tests")
            .EnableSensitiveDataLogging()
            .UseLoggerFactory(ContextLoggerFactory);
}

public class Blog
{
    public int Id { get; set; }
    [Column(TypeName = "jsonb")]
    public Dictionary<string, Entry> Entries { get; set; }
}

public class Entry
{
    public string Foo { get; set; }
}

在数据库中:

test=# select * from "Blogs"
test-# ;
 Id | Name |         Entries          
----+------+--------------------------
  1 |      | {"bla": {"Foo": "foo1"}}
(1 row)

【讨论】:

  • 这很奇怪,它看起来和我想要做的非常相似,我现在看不出任何相关的差异。我将不得不更仔细地将其与我自己的版本进行比较,看看我做了什么不同。感谢您提供一个完整的独立示例,这对实验很有帮助。
猜你喜欢
  • 1970-01-01
  • 2021-08-28
  • 1970-01-01
  • 2021-12-27
  • 1970-01-01
  • 1970-01-01
  • 2010-09-12
  • 2017-07-26
  • 2010-12-31
相关资源
最近更新 更多