5

I have a collection in my database where I log events. Each type of event has a different set of data. I've defined this with the following class:

[CollectionName("LogEvent")]
public class LogEvent
{
    public LogEvent(string eventType)
    {
        EventType = eventType;
        EventData = new Dictionary<string, object>();
    }

    public string EventType { get; private set; }
    [BsonExtraElements]
    public IDictionary<string, object> EventData { get; private set; }
}

Now - this works pretty good to some extent. As long as the elements of the EventData dictionary are simple types...

var event = new LogEvent("JobQueues"){
    EventData = new Dictionary<string, object>(){
        { "JobId": "job-123" },
        { "QueueName": "FastLane" }
    }
}

_mongoCollection.InsertOne(event);

...I get mongo documents like

{
  _id: ObjectId(...),
  EventType: "JobQueued",
  JobId: "job-123",
  QueueName: "FastLane"
}

But as soon as I try to add a custom type to the dictionary, things stops working.

var event = new LogEvent("JobQueues"){
    EventData = new Dictionary<string, object>(){
        { "JobId": "job-123" },
        { "QueueName": "FastLane" },
        { "JobParams" : new[]{"param-1", "param-2"}},
        { "User" : new User(){ Name = "username", Age = 10} }
    }
}

This gives me errors like ".NET type ... cannot be mapped to BsonType."

If I remove the [BsonExtraElements] tag, and [BsonDictionaryOptions(DictionaryRepresentation.Document)] It will start serialize stuff without errors, but it will give me a complete different document which I don't like..

{
  _id: ObjectId(...),
  EventType: "JobQueued",
  EventData: {      
      JobId: "job-123",
      QueueName: "FastLane",
      User: {
       _t: "User",
       Name: "username",
       Age: 10
      },
      JobParams : {
       _t: "System.String[]",
       _v: ["param-1", "param-2"]
      }
   }
}

What I want, is the following result:

{
  _id: ObjectId(...),
  EventType: "JobQueued",
  JobId: "job-123",
  QueueName: "FastLane",
  User: {
    Name: "username",
    Age: 10
  },
  JobParams : ["param-1", "param-2"]
}

Does anyone know how to achieve that?

(I'm using the C# mongodriver v2.3)

Maksim Simkin
  • 9,561
  • 4
  • 36
  • 49
Vegar
  • 12,828
  • 16
  • 85
  • 151

1 Answers1

3

So works MongoDriver, because it needs information of the type to deserilize it back. What you could do, is to write and register your own CustomMapper for User class:

public class CustomUserMapper : ICustomBsonTypeMapper
{
    public bool TryMapToBsonValue(object value, out BsonValue bsonValue)
    {
        bsonValue = ((User)value).ToBsonDocument();
        return true;
    }
}

Somewhere on starting program:

BsonTypeMapper.RegisterCustomTypeMapper(typeof(User), new CustomUserMapper());

That will work, and i have managed to serialize your data exact as you want.

But: As you want to deserialize it back, you wil get your User class as Dictionary, because driver will have no information about hiow to deserialize it:

enter image description here

Maksim Simkin
  • 9,561
  • 4
  • 36
  • 49
  • Deserialization is not an issue. This is an 'append only' collection, and in the rare condition where something is read back, it will be through custom projections. Anyway - custom serializers isn't a good option, since one point of this is to being able to add 'whatever' to the log. – Vegar Jan 26 '17 at 22:02
  • I had kind of a workaround going, by serializing the data as json with json.net first, and then ask mongo to create a bsondocument of the json. It kinda worked, but not an optimal solution... – Vegar Jan 26 '17 at 22:05
  • @Vegar Perhaps your workaround is the best solution. MongoDrive could add whatever to teh collection, but it want to be sure to be possible to get it back in exact the sam form, and for that is uses typedescriptors. – Maksim Simkin Jan 27 '17 at 07:25