Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

is it possible to store recursive data types as json column? #33618

Open
darena-antonw opened this issue Apr 26, 2024 · 1 comment
Open

is it possible to store recursive data types as json column? #33618

darena-antonw opened this issue Apr 26, 2024 · 1 comment

Comments

@darena-antonw
Copy link

Include your code

I have class that can contain it self or other types that can contain that same class, is it possible to store this type as json in a column with ef core?

I'd expect to have something like:

modelBuilder.Entity<Product>()
            .OwnsMany(
                x => x.Components,
                navBuilder => navBuilder.ToJsonTheWhoooooleDamnThing()
            );

instead I have to configure each navigation manually, but

  • if configuring navigations of types which have already been configured - exception is thrown
  • if not configuring these navigations data is incorrectly stored in the databse
  • furthermore, included a screenshot, for some reason ef core attached the parent object to the child property

Sample:

using Microsoft.EntityFrameworkCore;

{
    var appDbContext = new AppDbContext();

    appDbContext.Database.EnsureDeleted();
    appDbContext.Database.EnsureCreated();

    appDbContext.Products.Add(new Product()
    {
        Name = "test",
        Components =
        [
            new Component() { Name = Guid.NewGuid().ToString() },
            new Component()
            {
                Side = new Component() { Name = Guid.NewGuid().ToString() },
                Name = Guid.NewGuid().ToString(),
            },
            new Component()
            {
                Name = Guid.NewGuid().ToString(),
                Components =
                [
                    new Component()
                    {
                        Name = Guid.NewGuid().ToString(),
                        Side = new Component() { Name = Guid.NewGuid().ToString(), Components = [] },
                    },
                    new Component() { Name = Guid.NewGuid().ToString() },
                    new Component() { Name = Guid.NewGuid().ToString() }
                ]
            },
        ],
    });

    appDbContext.SaveChanges();
}
{
    var appDbContext = new AppDbContext();
    var products = appDbContext.Products.ToList();

    Console.WriteLine(products);
}

public class AppDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseNpgsql("...");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Product>()
            .OwnsMany(
                x => x.Components,
                navBuilder =>
                {
                    navBuilder.ToJson();
                    navBuilder.OwnsMany(x => x.Components);

                    // can't configure further, exception is thrown
                    // navBuilder.OwnsMany(x => x.Components, builder =>
                    // {
                    //     builder.OwnsMany(x => x.Components);
                    //     builder.OwnsOne(x => x.Side);
                    // });
                    
                    navBuilder.OwnsOne(x => x.Side);
                }
            );
    }
}


public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Component> Components { get; set; }
}

public class Component
{
    public string Name { get; set; }
    public List<Component> Components { get; set; }
    public Component Side { get; set; }
}

Stored json:

[
  {
    "Name": "4741f8a5-b7eb-4f1a-ad89-67654d305199",
    "Side": null,
    "Components": null
  },
  {
    "Name": "8077e443-cdd3-4746-97fa-c40d33e12686",
    "Side": {
      "Name": "a35dc642-fe01-43fb-86f2-01c3b87f3734"
    },
    "Components": null
  },
  {
    "Name": "4651ee19-3215-46c0-9e20-0fafd62fbc25",
    "Side": null,
    "Components": [
      {
        "Name": "886583d3-4cca-4abf-87a0-65b8a41ab8e1"
      },
      {
        "Name": "6aa899eb-0d14-4ecf-9f1f-95c9b61d5620"
      },
      {
        "Name": "aeefadac-817f-44c1-84b0-d154423896ca"
      }
    ]
  }
]

retrieved data:
image

Include provider and version information

EF Core version: 8.0.2
Database provider: Npgsql.EntityFrameworkCore.PostgreSQL 8.0.2
Target framework: .NET 8.0
Operating system: 14.2.1 (23C71)
IDE: Rider 2024.1.1

@AndriySvyryd
Copy link
Member

AndriySvyryd commented Apr 26, 2024

I have class that can contain it self or other types that can contain that same class, is it possible to store this type as json in a column with ef core?

Component references itself, so if we were to keep mapping it automatically it would go on forever.

Note to implementor: review naming logic for Product.Components.Components and invertedOwnership removal in InternalForeignKeyBuilder

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants