dotnet / dotnet/efcore

Non-nullable json array fields are translating to nullable in migration files

Open
#35,540 4 comments 0 reactions 1 assignee Claimed by @AndriySvyryd View on GitHub
area-json area-model-building
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

### Bug description

I think there is an issue, but i don't know if this issue related to this repo or to npgsql. When i describe a column field as `Object[]` with default value `[]` and creating a migration - this field in a migration file described as nullable unlike i was expect. I have a simple repro example. When i run `dotnet ef migrations add Test` it generate following migration file:

```csharp
using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace Test.App2.Migrations
{
///
public partial class Test : Migration
{
///
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "table",
columns: table => new
{
id = table.Column(type: "uuid", nullable: false),
value = table.Column(type: "text", nullable: false),
data_array = table.Column(type: "jsonb", nullable: true),
data_object = table.Column(type: "jsonb", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_table", x => x.id);
});
}

///
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "table");
}
}
}
```

If it is not an issue can you show me any workarounds to describe `data_array` as non nullable?

### Your code

```csharp
// See https://aka.ms/new-console-template for more information

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;

Console.WriteLine("Hello, World!");

public class Context(DbContextOptions options) : DbContext(options)
{
public DbSet Tables { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity()
.OwnsMany(e => e.DataArray, b => b.ToJson("data_array"));

modelBuilder.Entity()
.OwnsOne(e => e.DataObject, b => b.ToJson("data_object"));
}
}

public class ContextFactory : IDesignTimeDbContextFactory
{
public Context CreateDbContext(string[] args)
{
const string connStr = "Host=localhost;Port=5432;Database=test;Username=test;Password=test;";

var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseNpgsql(connStr);

return new Context(optionsBuilder.Options);
}
}

[Table("table")]
public class Table
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("id")]
public Guid Id { get; set; }

[Column("value")]
public string Value { get; set; } = default!;

public TableData[] DataArray { get; set; } = [];

public TableData DataObject { get; set; } = default!;
}

public class TableData
{
[JsonPropertyName("data1")]
public string Data1 { get; set; }

[JsonPropertyName("data2")]
public int Data2 { get; set; }

[JsonPropertyName("data3")]
public decimal Data3 { get; set; }
}
```

### Stack traces

```text

```

### Verbose output

```text
Using project '/home/ivan/Repositories/test/src/Test/Test.App2/Test.App2.csproj'.
Using startup project '/home/ivan/Repositories/test/src/Test/Test.App2/Test.App2.csproj'.
Writing '/home/ivan/Repositories/test/src/Test/Test.App2/obj/Test.App2.csproj.EntityFrameworkCore.targets'...
dotnet msbuild /target:GetEFProjectMetadata /property:EFProjectMetadataFile=/tmp/tmpbQDDcJ.tmp /verbosity:quiet /nologo /home/ivan/Repositories/test/src/Test/Test.App2/Test.App2.csproj
Writing '/home/ivan/Repositories/test/src/Test/Test.App2/obj/Test.App2.csproj.EntityFrameworkCore.targets'...
dotnet msbuild /target:GetEFProjectMetadata /property:EFProjectMetadataFile=/tmp/tmpMbBPWd.tmp /verbosity:quiet /nologo /home/ivan/Repositories/test/src/Test/Test.App2/Test.App2.csproj
Build started...
dotnet build /home/ivan/Repositories/test/src/Test/Test.App2/Test.App2.csproj /verbosity:quiet /nologo /p:PublishAot=false
/home/ivan/Repositories/test/src/Test/Test.App2/Program.cs(57,19): warning CS8618: свойство "Data1", не допускающий значения NULL, должен содержать значение, отличное от NULL, при выходе из конструктора. Возможно, стоит объявить свойство как допускающий значения NULL. [/home/ivan/Repositories/test/src/Test/Test.App2/Test.App2.csproj]

Сборка успешно завершена.

/home/ivan/Repositories/test/src/Test/Test.App2/Program.cs(57,19): warning CS8618: свойство "Data1", не допускающий значения NULL, должен содержать значение, отличное от NULL, при выходе из конструктора. Возможно, стоит объявить свойство как допускающий значения NULL. [/home/ivan/Repositories/test/src/Test/Test.App2/Test.App2.csproj]
Предупреждений: 1
Ошибок: 0

Прошло времени 00:00:00.88
Build succeeded.
dotnet exec --depsfile /home/ivan/Repositories/test/src/Test/Test.App2/bin/Debug/net8.0/Test.App2.deps.json --additionalprobingpath /home/ivan/.nuget/packages --runtimeconfig /home/ivan/Repositories/test/src/Test/Test.App2/bin/Debug/net8.0/Test.App2.runtimeconfig.json /home/ivan/.dotnet/tools/.store/dotnet-ef/8.0.6/dotnet-ef/8.0.6/tools/net8.0/any/tools/netcoreapp2.0/any/ef.dll migrations add Test --assembly /home/ivan/Repositories/test/src/Test/Test.App2/bin/Debug/net8.0/Test.App2.dll --project /home/ivan/Repositories/test/src/Test/Test.App2/Test.App2.csproj --startup-assembly /home/ivan/Repositories/test/src/Test/Test.App2/bin/Debug/net8.0/Test.App2.dll --startup-project /home/ivan/Repositories/test/src/Test/Test.App2/Test.App2.csproj --project-dir /home/ivan/Repositories/test/src/Test/Test.App2/ --root-namespace Test.App2 --language C# --framework net8.0 --nullable --working-dir /home/ivan/Repositories/test/src/Test/Test.App2 --verbose
Using assembly 'Test.App2'.
Using startup assembly 'Test.App2'.
Using application base '/home/ivan/Repositories/test/src/Test/Test.App2/bin/Debug/net8.0'.
Using working directory '/home/ivan/Repositories/test/src/Test/Test.App2'.
Using root namespace 'Test.App2'.
Using project directory '/home/ivan/Repositories/test/src/Test/Test.App2/'.
Remaining arguments: .
The Entity Framework tools version '8.0.6' is older than that of the runtime '8.0.11'. Update the tools for the latest features and bug fixes. See https://aka.ms/AAc1fbw for more information.
Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Found IDesignTimeDbContextFactory implementation 'ContextFactory'.
Found DbContext 'Context'.
Finding application service provider in assembly 'Test.App2'...
Finding Microsoft.Extensions.Hosting service provider...
No static method 'CreateHostBuilder(string[])' was found on class 'Program'.
No application service provider was found.
Finding DbContext classes in the project...
Using DbContext factory 'ContextFactory'.
Using context 'Context'.
Finding design-time services referenced by assembly 'Test.App2'...
Finding design-time services referenced by assembly 'Test.App2'...
No referenced design-time services were found.
Finding design-time services for provider 'Npgsql.EntityFrameworkCore.PostgreSQL'...
Using design-time services from provider 'Npgsql.EntityFrameworkCore.PostgreSQL'.
Finding IDesignTimeServices implementations in assembly 'Test.App2'...
No design-time services were found.
The index {'TableId'} was not created on entity type 'Table.DataArray#TableData (TableData)' as the properties are already covered by the index {'TableId', 'Id'}.
The property 'Table.DataArray#TableData (TableData).TableId' was created in shadow state because there are no eligible CLR members with a matching name.
The property 'Table.DataArray#TableData (TableData).Id' was created in shadow state because there are no eligible CLR members with a matching name.
The property 'Table.DataObject#TableData (TableData).TableId' was created in shadow state because there are no eligible CLR members with a matching name.
Writing migration to '/home/ivan/Repositories/test/src/Test/Test.App2/Migrations/20250128134308_Test.cs'.
Writing model snapshot to '/home/ivan/Repositories/test/src/Test/Test.App2/Migrations/ContextModelSnapshot.cs'.
'Context' disposed.
Done. To undo this action, use 'ef migrations remove'
```

### EF Core version

8.0.11

### Database provider

Npgsql.EntityFrameworkCore.PostgreSQL

### Target framework

.NET 8.0

### Operating system

Ubuntu 22.04.4 LTS

### IDE

JetBrains Rider 2024.1.3

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.