Search code examples
.netentity-frameworkasp.net-core.net-coreentity-framework-core

What configurations do I have to add to my DbContext to store property of type IDictionary<string, object>


I have a domain entity in my code where i have property Parameters which is of type Dictionary<string, object>. I have tried to add JsonConvert. But the data gets changed while storing in db.

Task.cs

public sealed class Task
{
    
    public Guid TaskId { get; set; }
    public Guid QueryId { get; set; }
    public Guid DatabaseId { get; set; }
    public Guid OrganizationId { get; set; }
    public DateTime CreatedOn = DateTime.UtcNow;
    public string Status { get; set; } = string.Empty;
    public IDictionary<string, object> Parameters { get; set; } = new Dictionary<string, object>();
}

DbContext.cs

public interface IDbContext
{
    DbSet<Task> Tasks { get; }
    Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess = true, CancellationToken cancellationToken = default);
}

internal sealed class QueryHubDbContext : DbContext, IQueryHubDbContext
{
    public QueryHubDbContext(DbContextOptions<QueryHubDbContext> options)
        : base(options)
    {
    }
    public DbSet<Task> Tasks => Set<Task>();
   

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Query>(entity =>
        {
            entity.Property(e => e.Id).ValueGeneratedNever();
            entity.Property(e => e.Tags)
            .HasConversion(
                v => string.Join(',', v.Where(tag => !string.IsNullOrWhiteSpace(tag))),
                v => v.Split(',', StringSplitOptions.RemoveEmptyEntries));
        });

        modelBuilder.Entity<Task>(ConfigureTask);

        base.OnModelCreating(modelBuilder);
    }

    private void ConfigureTask(EntityTypeBuilder<Task> builder)
    {
        builder.HasKey(e => e.TaskId);
        builder.Property(e => e.TaskId).ValueGeneratedNever();
         builder.Property(e => e.Parameters)
            .HasConversion(
                v => JsonConvert.SerializeObject(v),
                v => JsonConvert.DeserializeObject<Dictionary<string, object>>(v) ?? new Dictionary<string, object>())
            .HasColumnType("nvarchar(max)");
    }
}

with these configuration i when i pass

{
  "taskId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "queryId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "databaseId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "organizationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "string",
  "parameters": {
    "additionalProp1": "string",
    "additionalProp2": "string",
    "additionalProp3": "string"
  }
}

Data stored in the db ->


{
  "taskId": "9abf0000-76ab-40b0-d1d5-08dbf0421eaa",
  "queryId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "databaseId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "organizationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "",
  "parameters": {
    "additionalProp1": {
      "valueKind": []
    },
    "additionalProp2": {
      "valueKind": []
    },
    "additionalProp3": {
      "valueKind": []
    }
  }
}

I am using SqlServer as my Db. There exist a db type "json" or "jsonb" for postgres. There doesnt for sql server.


Solution

  • I tested pass the data with some difference having no problem.
    Task.cs

        public class Task
        {
            [Key]
            public int Id { get; set; }
            public Dictionary<string, object> Parameters { get; set; } = new Dictionary<string, object>();
        }
    

    MyContext.cs

        public class MyContext : DbContext
        {
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                optionsBuilder.UseMySql("server=192.168.2.68;database=wa76;user=mysql1;password=xxxxx", new MySqlServerVersion(new Version()));
            }
    
            public DbSet<Task> TaskTable { get; set; }
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
    
                modelBuilder.Entity<Task>()
                    .Property(b => b.Parameters)
                    .HasConversion(
                        v => JsonConvert.SerializeObject(v),
                        v => JsonConvert.DeserializeObject<Dictionary<string, object>>(v));
            }
        }
    

    controller

            [HttpPost("test")]
            public void test(object obj)
            {
                var model = JsonConvert.DeserializeObject<Task>(obj.ToString());
                _context.Add(model);
                _context.SaveChanges();
            }
    

    Test
    Postman:
    enter image description here
    Result:
    enter image description here