Зависимый от схемы класс для безсхемного документа с использованием MongoDb и C # - PullRequest
2 голосов
/ 04 августа 2011

Предположим, у нас есть документ для хранения нашего клиента, в котором есть фиксированные и дополнительные поля. Итак, вот наш пример класса для клиента:

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

В классе дополнительного поля у нас есть что-то вроде этого:

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

Если бы я использовал стандартное поведение драйвера для сериализации, я бы получил что-то вроде этого:

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

Хотя хотелось бы что-то вроде этого:

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

Это улучшит производительность поиска и, как правило, является точкой ориентации документа.

Как я могу настроить сериализацию таким образом?

Ответы [ 2 ]

1 голос
/ 05 августа 2011

Вот как я это решил (работает нормально) и решил проблему.

using System; 
using System.Collections.Generic; 
using System.Linq; using System.Text; 
using MongoDB.Bson;
using MongoDB.Bson.Serialization; 
using MongoDB.Bson.Serialization.Serializers;

namespace TestDataGeneration {
    public class FieldsWrapper : IBsonSerializable
    {
        public List<DataFieldValue> DataFieldValues { get; set; }


        public object Deserialize(MongoDB.Bson.IO.BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options)
        {
        if (nominalType != typeof(FieldsWrapper)) throw new ArgumentException("Cannot deserialize anything but self");
        var doc = BsonDocument.ReadFrom(bsonReader);
        var list = new List<DataFieldValue>();
        foreach (var name in doc.Names)
        {
            var val = doc[name];
            if (val.IsString)
                list.Add(new DataFieldValue {LocalIdentifier = name, Values = new List<string> {val.AsString}});
            else if (val.IsBsonArray)
            {
                DataFieldValue df = new DataFieldValue {LocalIdentifier = name};
                foreach (var elem in val.AsBsonArray)
                {
                    df.Values.Add(elem.AsString);
                }
                list.Add(df);
            }
        }
        return new FieldsWrapper {DataFieldValues = list};
        }


        public void Serialize(MongoDB.Bson.IO.BsonWriter bsonWriter, Type nominalType, IBsonSerializationOptions options)
        {
            if (nominalType != typeof (FieldsWrapper))
                throw new ArgumentException("Cannot serialize anything but self");
            bsonWriter.WriteStartDocument();
            foreach (var dataFieldValue in DataFieldValues)
            {

                bsonWriter.WriteName(dataFieldValue.LocalIdentifier);
                if (dataFieldValue.Values.Count != 1)
                {
                    var list = new string[dataFieldValue.Values.Count];
                    for (int i = 0; i < dataFieldValue.Values.Count; i++)
                        list[i] = dataFieldValue.Values[i];
                    BsonSerializer.Serialize(bsonWriter, list); 
                }
                else
                {
                    BsonSerializer.Serialize(bsonWriter, dataFieldValue.Values[0]); 
                }
            }
            bsonWriter.WriteEndDocument();
        }

    } }
0 голосов
/ 04 августа 2011

По сути, вам просто нужно реализовать два метода самостоятельно.Первый - сериализация объекта по вашему желанию, а второй - десериализация объекта из базы данных в класс Client обратно:

1. Класс клиента Seialize:

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. Десериализация объекта клиента:

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 Полный пример теста:

Я добавил два выше метода к вашему клиентскому классу.

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 Структура данных в базе данных:

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

Кстати: взгляните на учебник по сериализации .

...