39

I am using Entity Framework Core with npgsql postgresql for Entity Framework Core.

My question is, using migrations, how do I mark a class property to generate a JSONB column type?

For example:

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

    // My JSONB column
    public string Data { get; set; }
}

Thanks in advance.

3
  • 3
    Have you tried Byte[] and in the entity mapping set column type to jsonb ? Commented Mar 29, 2017 at 14:58
  • Were you able to query jsonb using ef core? e.g. to query properties of json doc Commented Nov 29, 2017 at 1:01
  • zaitsman, think that is not possible at this moment. Look at github.com/aspnet/EntityFrameworkCore/issues/4021 . I have a sql query for that. I am using this to read the result: github.com/aspnet/EntityFrameworkCore/issues/… . I already found an issue with this code, related with DateTimeOffset and DateTime. If you hit this issue, i can show you my workaround. Commented Nov 29, 2017 at 10:21

3 Answers 3

52

Based on H. Herzl comment:

My final solution was something like this:

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

    [Column(TypeName = "jsonb")]
    public string Data { get; set; }
}

Migrations generated this:

Data = table.Column<string>(type: "jsonb", nullable: true),

When updated the database with migrations, the Data column was created correctly with jsonb type.

Sign up to request clarification or add additional context in comments.

8 Comments

@Alex Klaus this link is unfortunately dead.
@wegry, here is the link - npgsql.org/efcore/mapping/…
When I tried to do this, I added a migration (add-migration), then I did update-database and I got the error "42804: column "my_olumn" cannot be cast automatically to type json" - so I dropped the table and let EF code first re-create them ("update-database"), but again no luck. The column my_olumn existed before as a string. Can you help? What is wrong? By the way, after "jsonb" was failing, I also tried "json" - same error.
@Matt, that is strange. How is possible that the column already exists if you dropped the table?
@bruno.almeida - that is due to the "migration history" - EF.Core Code First keeps every step and I had that column "my_column" as text there in previous migration steps. So what I found out is that EF Core is trying to convert "text" into "jsonb" - which fails. Even in pgAdmin it is not possible to select jsonb if you edit the table and want to change the column datatype. The only thing that worked is if you create a new column of type "jsonb" with a different name and then delete the old column. If the column didn't exist before, then you're lucky. Maybe throwing away some migr. steps helps.
|
26

using string as was suggested by @bruno.almeida is a nice solution but couldn't be queried.

additional approaches are to use:

  • As System.Text.Json DOM types (JsonDocument or JsonElement)
  • As strongly-typed user-defined types (POCOs)

JsonDocument being my favorite since it could be queried, is flexible and fast to setup, e.g.:

public JsonDocument Customer { get; set; }

more details at: https://www.npgsql.org/efcore/mapping/json.html

2 Comments

Nice write-up @ link regarding the available options.
A warning for future readers: JsonDocument needs to be disposed, or you will leak memory. This means any entities with a JsonDocument property that you query will need to be disposed of as well.
1

In addition to the response by @bruno.almeida, it is worth noting that it is possible to add a type to the JSONB column. For example, I have recently been creating a column to hold various file upload errors:

I have a RecordErrors class that I think is generic enough to be used in many of my error storage situations:

public class RecordErrors
{
    public IEnumerable<ResultError> Errors { get; set; } = Enumerable.Empty<ResultError>();
}

The ResultError class just has additional properties such as ErrorCode and Message but could just as easily be replaced with String if you wanted simple string error values.

My Entity then looks like this:

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

    [StringLength(800)]
    public string Name { get; set; } = string.Empty;
    ...

    [Column(TypeName = "jsonb")]
    public RecordErrors? Errors { get; set; } = null;
}

Running migrations created the column with the jsonb type.

You can then save data to the database in the usual way using:

 _databaseContext.SaveChangesAsync(cancellationToken);

The result in the database in my instance is JOSN that looks something like:

{
  "Errors": [
    {
      "Code": "Columns.Unsupported",
      "Message": "The upload contains the following unsupported column headings: Column 1, Column 2, Column 3, Column 4."
    }
  ]
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.