efcore icon indicating copy to clipboard operation
efcore copied to clipboard

How to deal with dictionary items in entity for .ToJson functionality

Open MacMcDell opened this issue 2 years ago • 1 comments

How to deal with dictionary items in Json?

I am banging my head against a wall trying to get a dictionary within a poco to behave nicely when storing the data as Json. I am using EFCore 7.08. Why does this seem so difficult to get done? What am I missing to understand here?. MatchSuccess Has/Owns? a property that is a collection. All I want to get stored is something that looks like reasonable json example of what I want in db:

[{ "Foo": "222",    "Bar": "269506",    "Meta": {      "SpaceId": "123728"  }  }] //The Meta prop is the issue.

vs what I get in db:

[{"Meta":"{\u0022SpaceId\u0022:\u0022123728\u0022}","Foo":"222","Bar":"269506"}]

As you cannot see... I can easily parse this back out.

setup

I have a very simple POCO like this:

public class MatchSuccess
{
    public MatchSuccess()
    {
        Meta = new Dictionary<string, string>();
    }
   
    public string Foo{ get; set; }
    public string Bar{ get; set; }
    public Dictionary<string, string> Meta { get; set; } // <<<<This is a mess
}

It gets used here:

public class DtoObject: 
{
    public int Id { get; set; }
    public List<MatchSuccess> MatchSuccesses { get; set; } = new List<MatchSuccess>(); //<<<It is a list
    public List<MatchFailure> MatchFailures { get; set; } = new List<MatchFailure>();
}

my builder looks like this:

 modelBuilder.Entity<DtoObject>(dto=>
        {
            dto.OwnsMany(e=> e.MatchSuccesses, matchSuccessBuilder =>
            {
                matchSuccessBuilder.ToJson();
                //matchSuccessBuilder.OwnsOne(matchSuccess => matchSuccess.Meta, metaBuilder => { metaBuilder.ToJson();}); 
               // creates :   @p1='[{"Foo":"236129","Bar":"888","Meta":{}}]' (Nullable = false) 
               
                 matchSuccessBuilder.HasOne(matchSuccess => matchSuccess.Meta);
                ^^^ Throws exception
                
               // matchSuccessBuilder.OwnsOne<Dictionary<string,string>>(c => c.Meta, b =>  { b.ToJson(); });
               // creates:  @p1='[{"Foo":"236129","Bar":"888","Meta":{}}]'
               
                 // matchSuccessBuilder.Property(p => p.Meta)
                //     .HasConversion(
                //         v => JsonSerializer.Serialize(v, options),
                //         v => JsonSerializer.Deserialize<Dictionary<string, string>>(v, options));
               // creates:  @p1='[{"Meta":"{\u0022SpaceId\u0022:\u0022236129\u0022}","Foo":"236129","Bar":"888"}]' 

            });
            dto.OwnsMany(e => e.MatchFailures, b =>  {  b.ToJson();   });

my serializer options are:

 var options = new JsonSerializerOptions()
        {
            PropertyNameCaseInsensitive = true,
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
            WriteIndented = false,
           Encoder  = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
        };

This thing is slowly killing me.

Include provider and version information

EF Core version: 7.08 Database provider: (e.g. Microsoft.EntityFrameworkCore.SqlServer) Target framework: .NET 6.0 Operating system: IDE: Rider

MacMcDell avatar Jul 13 '23 14:07 MacMcDell

Note for triage: likely covered by #31257, but not covered by #29427.

ajcvickers avatar Jul 15 '23 15:07 ajcvickers

@ajcvickers i read the other post regarding primitives and see there was some commits to ef8. Is using the property / conversion the only supported way to do what I need in ef7? Does ef8 support this now to have just a nice array of string,string?

MacMcDell avatar Jul 16 '23 13:07 MacMcDell

@MacMcDell EF8 will bring 1st-class support for primitive lists/arrays, but not for dictionaries. In both EF7 and 8, to serialize a dictionary you'll have to use a value converter.

roji avatar Jul 16 '23 15:07 roji

Thanks for getting back to me. So the current method I'm using where I serialize manually in and out is pretty much the recommended way? Ie... This.property.hasconversion(in,out)...

On Sun, 16 Jul 2023, 08:05 Shay Rojansky, @.***> wrote:

@MacMcDell https://github.com/MacMcDell EF8 will bring 1st-class support for primitive lists/arrays, but not for dictionaries. In both EF7 and 8, to serialize a dictionary you'll have to use a value converter.

— Reply to this email directly, view it on GitHub https://github.com/dotnet/efcore/issues/31257#issuecomment-1637115366, or unsubscribe https://github.com/notifications/unsubscribe-auth/AALRK5NQKQPDQE6MLIODXVLXQP7L3ANCNFSM6AAAAAA2JAISXU . You are receiving this because you were mentioned.Message ID: @.***>

MacMcDell avatar Jul 16 '23 16:07 MacMcDell

Yes.

roji avatar Jul 16 '23 17:07 roji

Duplicate of #29825

ajcvickers avatar Jul 26 '23 19:07 ajcvickers

@ajcvickers it's not a weakly-typed mapping. Seems like I ran into the same issue with strongly typed ImmutableDictionary :(

vchirikov avatar Mar 20 '24 14:03 vchirikov