Search code examples
c#mongodbmongodb-.net-driverschemaless

Schema dependent class to schemaless document using MongoDb and C#


Let us suppose we have a document to store our client which has fixed and extra fields. So here goes our sample class for the client:

public class Client
{
     public string Name{ get; set; }
     public string Address{ get; set; }
     public List<ExtraField> ExtraFields{ get; set; } //these fields are extra ones
}

In extra field class we have something like this:

public class ExtraField
{
    public string Key{ get; set; }
    public string Type { get; set; }
    public string Value { get; set; }
}

If I use standard driver's behaviour for serialization I would get smth like this:

{{Name:VName, Address:VAddress,  ExtraFields:[{Key:VKey,Type:VType,
Value:VValue},...]}, document2,...,documentn}

While I would like to have something like this:

{{Name:VName, Address:VAddress, VKey:VValue,...}, document2,...,documentn}

This would improve the search performance and is generally the point of document orientation.

How can I customize the serialization to such a way?


Solution

  • Essentially you just need to implement two methods yourself. First one to serialize an object as you want and second to deserialize an object from db to your Client class back:

    1 Seialize client class:

    public static BsonValue ToBson(Client client)
    {
      if (client == null)
        return null;
    
      var doc = new BsonDocument();
      doc["Name"] = client.Name;
      doc["Address"] = client.Address;
      foreach (var f in client.ExtraFields)
      {
        var fieldValue = new BsonDocument();
        fieldValue["Type"] = f.Type;
        fieldValue["Value"] = f.Value;
        doc[f.Key] = fieldValue;
      }
    
      return doc;
    }
    

    2 Deserialize client object:

    public static Client FromBson(BsonValue bson)
    {
      if (bson == null || !bson.IsBsonDocument)
        return null;
    
      var doc = bson.AsBsonDocument;
    
      var client = new Client
      {
        ExtraFields = new List<ExtraField>(),
        Address = doc["Address"].AsString,
        Name = doc["Name"].AsString
      };
      foreach (var name in doc.Names)
      {
        var val = doc[name];
        if (val is BsonDocument)
        {
          var fieldDoc = val as BsonDocument;
          var field = new ExtraField
          {
            Key = name,
            Value = fieldDoc["Value"].AsString,
            Type = fieldDoc["Type"].AsString
          };
           client.ExtraFields.Add(field);
         }
       }
    
     return client;
    }
    

    3 Complete test example:

    I've added above two method to your client class.

    var server = MongoServer.Create("mongodb://localhost:27020");
    var database = server.GetDatabase("SO");
    
    var clients = database.GetCollection<Type>("clients");
    
    
    var client = new Client() {Id = ObjectId.GenerateNewId().ToString()};
    client.Name = "Andrew";
    client.Address = "Address";
    client.ExtraFields = new List<ExtraField>();
    client.ExtraFields.Add(new ExtraField()
    {
      Key = "key1",
      Type = "type1",
      Value = "value1"
    });
    client.ExtraFields.Add(new ExtraField()
    {
      Key = "key2",
      Type = "type2",
      Value = "value2"
    });
    
     //When inseting/saving use ToBson to serialize client
    clients.Insert(Client.ToBson(client));
    
    //When reading back from the database use FromBson method:
    var fromDb = Client.FromBson(clients.FindOneAs<BsonDocument>());
    

    4 Data structure in a database:

    {
      "_id" : ObjectId("4e3a66679c66673e9c1da660"),
      "Name" : "Andrew",
      "Address" : "Address",
      "key1" : {
        "Type" : "type1",
        "Value" : "value1"
      },
      "key2" : {
        "Type" : "type2",
        "Value" : "value2"
      }
    }
    

    BTW: Take a look into serialization tutorial as well.