npgsql / efcore.pg

Entity Framework Core provider for PostgreSQL
PostgreSQL License
1.52k stars 223 forks source link

JSON POCO Save Changes does not update props in JSON #1228

Closed arif-hanif closed 4 years ago

arif-hanif commented 4 years ago

Version v3.1.0

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace API.DesignHub.Entities
{
    public class DesignHubProject
    {
        [Key]
        public string Number { get; set; }
        public string Name { get; set; }
        [Column(TypeName = "jsonb")]
        public ProjectSectionStatus SectionStatuses { get; set; }
    }
}
using System.Text.Json.Serialization;

namespace API.DesignHub.Entities
{
    public class ProjectSectionStatus
    {
        public ProjectSectionStage ExecutiveSummarySectionStatus { get; set; } 
        public ProjectSectionStage CodesAndGuidelinesSectionStatus { get; set; }
        public bool IsMechanicalComplete { get; set; } = false;
    }
}
using System.Runtime.Serialization;

namespace API.DesignHub.Entities
{
    public enum ProjectSectionStage
    {
        NOT_STARTED,
        INCOMPLETE,
        COMPLETE,
        NEEDS_REVIEW,
        NOT_APPLICABLE
    }
}
var designHubProject = _dbContext.DesignHubProjects.SingleOrDefault(
                dhp => dhp.Number == projectNumber);

designHubProject.ExecutiveSummarySectionStatus = ProjectSectionStage.COMPLETE

 _dbContext.SaveChanges();

The DB does not get modified

bojan96 commented 4 years ago

I have same issue, updating any json property or adding element to array does not update json object in DB

roji commented 4 years ago

Because JSON documents can be arbitrarily large, we don't enable automatic change detection on them (more precisely, we don't define a value comparer that does value comparison). If we did, that means that would mean that we would do a snapshot of every JSON document loaded from the database, and a deep comparison against the snapshot once SaveChanges is called. This would be extremely bad for perf.

You can manually flag the JSON document as modified yourself:

ctx.Entry(designHubProject).Property(b => b.SectionStatuses).IsModified = true;

Note that your last snippet has designHubProject.ExecutiveSummarySectionStatus = ProjectSectionStage.COMPLETE, but ExecutiveSummarySectionStatus seems to be a property on SectionStatuses.

Note that support for change-tracking proxies has been implemented on the EF Core side; this means that when 5.0 comes out, you may be able to have automated change tracking without snapshot and comparison, by wrapping your JsonDocument/JsonElement by a proxy.

arif-hanif commented 4 years ago

thanks @roji was able to implement what you pointed out ctx.Entry(designHubProject).Property(b => b.SectionStatuses).IsModified = true;

TsengSR commented 6 months ago

Because JSON documents can be arbitrarily large, we don't enable automatic change detection on them (more precisely, we don't define a value comparer that does value comparison). If we did, that means that would mean that we would do a snapshot of every JSON document loaded from the database, and a deep comparison against the snapshot once SaveChanges is called. This would be extremely bad for perf.

You can manually flag the JSON document as modified yourself:

ctx.Entry(designHubProject).Property(b => b.SectionStatuses).IsModified = true;

Note that your last snippet has designHubProject.ExecutiveSummarySectionStatus = ProjectSectionStage.COMPLETE, but ExecutiveSummarySectionStatus seems to be a property on SectionStatuses.

That's retarded tbh and completely not in line with how rest of EF Core works and completely unexpected behavior. What's the proper way to get it handled?

And no, changing the state to modifed is NOT the proper way to solve it, EF Core is behind a repository and the code/Entity containing it doesn't have any knowledge of EF Core internals.