Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework: Serialize/Deserialize JSON column behind the scene

I have Code First class/table and one of the fields has type string/nvarchar. This string is JSON representation of MyClass instance. I'd like to operate in code with MyClass instances only, but store it as a string (JSON) in database. Let's say my table looks like this:

public class Message
{
    [Key]
    public int Id { get; set; }
    public string Title { get; set; }
    public string JsonDefinition { get; set; }
}

I'd prefer to have it like this

public class Message
{
    [Key]
    public int Id { get; set; }
    public string Title { get; set; }
    [JSON]
    public MyClass JsonDefinition { get; set; }
}

Where JSON is a custom attribute which tells EF to store the field as serialized string of MyClass instance. And at the same time it says EF: "once you pull the entity, replace JsonDefinition string with deserialized instance of MyClass"

Is it possible to achieve with existing EF 4 mechanisms? If so, then how?

Thanks in advance.

EDIT: MyClass could be a Dictionary or any other complex type.

like image 738
Pavel Podlipensky Avatar asked Jul 02 '12 23:07

Pavel Podlipensky


2 Answers

Not directly. You must always have your string property present in your class because EF demands it for persistence. You can also have your non mapped MyClass property but you must manually handle serialization and deserialization and makes those properties in sync.

Naive solution is to implement INotifyPropertyChanged in your MyClass and ensure that every change in MyClass value or in any of its properties will trigger JSON serialization to the string property. This naive solution works for some simple problems but in this case it is really bad idea because it can have big performance impact if you modify a lot of properties on assigned MyClass property.

Another way is using EF's hooks for materialization and saving changes. You will need to handle ObjectContext.ObjectMaterialized event (you can get ObjectContext from DbContext via IObjectContextAdapter explicitly implemented by DbContext). In this event handler you will use string property's value and deserialize its content to MyClass property. You will also have to override DbContext.SaveChanges where you will look for all Message instances which should be inserted or updated and use their MyClass property to get current value and serialize it to string property.

What you are looking for are some complex mapping scenarios or mapped conversions. EF doesn't support them but you can vote for my suggestion on Data UserVoice.

like image 165
Ladislav Mrnka Avatar answered Sep 21 '22 09:09

Ladislav Mrnka


I would try something as follows:

public class Message
{
    [Key]
    public int Id { get; set; }
    public string Title { get; set; }

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

    [NotMapped]
    public MyClass JsonDefinition 
    {
        get => JsonConvert.DeserializeObject<MyClass>(JMyClass );
        set => JMyClass = JsonConvert.SerializeObject(value);
    }
}
like image 24
user2137109 Avatar answered Sep 22 '22 09:09

user2137109