Home > database >  C# Store an object that has a dictionary as a field in SQL Database
C# Store an object that has a dictionary as a field in SQL Database

Time:09-27

I have a long list of crafted items with a reagents dictionary consisting of an int that is the id of a reagent and an int that is the quantity to use.

I have no idea how to store this list in tables.

public class CraftedItem : IEquatable<CraftedItem>
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public Dictionary<int, int> ReagentsAndQuantities { get; set; }
    public int RecipeId { get; set; }

    public bool Equals(CraftedItem other)
    {
        if (other is null)
            return false;

        return Id == other.Id;
    }

    public override bool Equals(object obj) => Equals(obj as Recipe);
    public override int GetHashCode() => Id.GetHashCode();
}

When I google it, I get lots of hits on how to use dictionaries but nothing on how to store a class which has a dictionary as a member. Can anyone point me in the right direction?

CodePudding user response:

I can't speak to EF at all, but if you are just using normal ADO connections, you just need a separate table for your dictionary:

create table crafted_item (
  id int primary key not null,
  name text,
  recipe_id int
);

create table reagents_and_quantities (
  crafted_item_id int not null,
  reagent int not null,
  quantity int,
  constraint reagents_and_quantities_pk primary key (crafted_item_id, reagent)
);

The trick is then your CRUD which you use to populate the data. I would think something as simple as this would work. Assuming your CRUD GetAll method looks something like this:

  List<CraftedItem> results = new List<CraftedItem>();

  using (NpgsqlCommand cmd = new NpgsqlCommand("select * from crafted_item", conn))
  {
      using (NpgSqlDataReader reader = cmd.ExecuteReader())
      {
          while (reader.Read())
          {
              CraftedItem foo = new CraftedItem();
              // populate the properties
              foo.ReagentsAndQuantities = new Dictionary<int, int>();
              results.Add(foo);
          }
          reader.Close();
      }
  }

Then, before you close your connection do another pass and dynamically bind the dictionary entries:

  using (NpgsqlCommand cmd = new NpgsqlCommand("select * from reagents_and_quantities", conn))
  {
      using (NpgSqlDataReader reader = cmd.ExecuteReader())
      {
          while (reader.Read())
          {
              int craftedItem = reader.GetInt32(0);
            
              CraftedItem foo = results.Find(x => x.Id == craftedItem);
              if (foo != null)
              {
                  int reagent = reader.GetInt32(1);
                  int qty = reader.GetInt32(2);
                  foo.ReagentsAndQuantities[reagent] = qty;
              }
          }
          reader.Close();
      }
  }

I got a little lazy, but hopefully you get the idea. Presumably your "GetAll" would have some form of where clause, so you would of course want to apply that to the reagents_and_quantities details to streamline.

Alternatively you could use a JSON datatype, but I wouldn't bother.

  • Related