使用MongoDb和C#的模式依赖类到无模式文档

时间:2011-08-04 08:33:48

标签: c# mongodb mongodb-.net-driver schemaless

让我们假设我们有一个文档来存储我们的客户端,其中包含固定和额外的字段。 所以这里是客户端的示例类:

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 个答案:

答案 0 :(得分:1)

这是我解决它的方式(它工作正常)并解决了这个问题。

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();
        }

    } }

答案 1 :(得分:0)

基本上你只需要自己实现两种方法。第一个是根据需要序列化对象,第二个是将对象从db反序列化为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"
  }
}

顺便说一句:同样看看serialization tutorial