dotnet / dotnet/EntityFramework.Docs

Document behaviors around setting owned navigations to null

Open
#4,669 5 comments 3 reactions 0 assignees View on GitHub
area-aggregates
Dominant language
Mermaid
Stars
1.7k
Forks
2k
Avg merge
7d 23h
Merged PRs (30d)
16

Description

## Structured data in nested Json can't be set to null

Currently it is not possible to update nested structured data in a json column to null, just by attaching an entity to the context.

I can understand this behavior for non json columns as it would be more involved to update the database. But for json columns the serialization should not be a merge of navigation and non navigation properties.

Given the example below. After attaching `owner` to the database and saving it, the `MoreData` property should be null.

To be clear the data we want to attach is coming via an asp controller.

To make this work right now, we would need to:

1. Check the incoming data for null in the field.
2. Save the state to a bool. (for example `isNull`)
3. If it `isNull` set it to a temporary value.
4. Attach the entity to the context.
5. Based on `isNull` remove the temporary value again.

Maybe there is another solution? So far we were not able to find one which did not involve dealing with the previous data or tricking the change tracker into recognizing the non null state first.

### Code

```C#
using System.Diagnostics;
using Microsoft.EntityFrameworkCore;

var myDbContext = new MyDbContext();
myDbContext.Database.EnsureDeleted();
myDbContext.Database.EnsureCreated();

// Create data and detach
var owner = myDbContext.JsonOwners.SingleOrDefault(o => o.Data.Name == "Data");
if (owner == null)
{
owner = new JsonOwner()
{
Data = new Data()
{
Name = "Data",
MoreData = new NestedData()
{
NestedName = "NestedData"
}
}
};
myDbContext.JsonOwners.Add(owner);
}
myDbContext.SaveChanges();
myDbContext.Entry(owner).State = EntityState.Detached;

// Set to nested data to null and re-attach
// In reality this data comes via an http call and is attached to the context.
owner.Data.MoreData = null;
myDbContext.Update(owner);
myDbContext.SaveChanges();

// Getting by name and asserting
var d = myDbContext.JsonOwners.Single(o => o.Data.Name == "Data");
Debug.Assert(d.Data.MoreData == null, "d.Data.MoreData == null");

return;

public class JsonOwner
{
public Guid Id { get; set; }

public Data Data { get; set; }
}

public class Data
{
public string Name { get; set; }
public NestedData? MoreData { get; set; }
}

public class NestedData
{
public string NestedName { get; set; }
}

public class MyDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EfLab;Trusted_Connection=True;");
}

public DbSet JsonOwners { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity(e =>
{
e.OwnsOne(p => p.Data, x =>
{
x.ToJson();
x.OwnsOne(r => r.MoreData);
});
});
}
}
```

### Version information

EF Core version: 8.0.1
Database provider: Microsoft.EntityFrameworkCore.SqlServer (8.0.1)
Target framework: .NET 8
Operating system: Windows 11

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.