Search code examples
c#jsonpostgresqlentity-framework-corenpgsql

Is there a way to use list with EF Core Postgres JSON?


This is possible in PG:

public class Parent
{
  [Column(TypeName = "jsonb")]
  //Mode 1: a column in the table
  public Child[] Children { get; set; }
}

public class Child
{
  //Mode 2: a virtual column only existing in JSON
  public GrandChild[] GrandChildren { get; set; }
}

public class GrandChild
{
}

My question if there is a way to use other CLR types inline, instead of arrays, such as List<T>, HashSet<T> or even just IList<T> or ICollection<T>, to enable easy access and to avoid recreation of the collection each time we want to make a change, or to avoid defining a bunch of other proxy properties.

I tried setting HasConversion to array but it didn't work.


Solution

  • This works "automatically" if you enable the type plugins in Npgsql.Json.NET:

    NpgsqlConnection.GlobalTypeMapper.UseJsonNet();
    using (var context = new MyContext(options.Options))
    {
        var parent = new Parent()
        {
            Children = {
                new Child() {
                    GrandChildren = {
                        new GrandChild() { Name = "A" },
                        new GrandChild() { Name = "B" }
                    }
                }
            }
        };
    
        context.Add(parent);
        context.SaveChanges();
    
        foreach(var p in context.Parents.ToList()) {
            // This is just to print the whole object. You don't have to touch JSON.NET
            // yourself here, Npgsql will convert to/from .net types at 'the edges'.
            Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(p));
        }       
    }
    
    
    // Using these definitions
    class MyContext : DbContext
    {
        public MyContext(DbContextOptions<MyContext> options)
           : base(options)
        { } 
        public DbSet<Parent> Parents { get; set; }
    }
    public class Parent
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
    
        [Column(TypeName = "jsonb")]
        public List<Child> Children { get; set; } = new List<Child>();
    }    
    public class Child
    {
        public List<GrandChild> GrandChildren { get; set; } = new List<GrandChild>();
    }    
    public class GrandChild
    {
        public string Name { get; set; }
    }