dotnet / dotnet/efcore

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

Open
#33,618 3 comments 5 reactions 1 assignee Claimed by @AndriySvyryd View on GitHub
area-model-building customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

### 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:
```cs
modelBuilder.Entity()
.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:

```C#
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 Products { get; set; }

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

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

modelBuilder.Entity()
.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 Components { get; set; }
}

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

Stored json:
```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

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.