17

Im working on a project that use .NET Razor and mongodb. I would like to do something like this:

@{
    var feeds = DP.Database.GetCollection("feeds").FindAll();
}
<ul>
    @foreach (dynamic feed in feeds)
    {
        <li>@feed.message - @feed.from.name</li>
    }
</ul>

However, the current mongodb C# driver FindAll() return collection of BsonDocument which does not support dynamic object. Anybody know a .NET 4 dynamic supported mongodb C# driver?

Thanks a lot

hoang
  • 193
  • 1
  • 2
  • 9

5 Answers5

18

I created a straight-forward extension to the MongoDB driver that re-serializes the BSON document using Json.NET and deserializes it as a dynamic. By including the following class, you can simply convert your MongoDB queries to dynamic like this

dynamic obj = collection.FindOneByIdAs<BsonDocument>(someObjectId).ToDynamic();

Extension class:

public static class MongoDynamic
{
    private static System.Text.RegularExpressions.Regex objectIdReplace = new System.Text.RegularExpressions.Regex(@"ObjectId\((.[a-f0-9]{24}.)\)", System.Text.RegularExpressions.RegexOptions.Compiled);
    /// <summary>
    /// deserializes this bson doc to a .net dynamic object
    /// </summary>
    /// <param name="bson">bson doc to convert to dynamic</param>
    public static dynamic ToDynamic(this BsonDocument bson)
    {
        var json = objectIdReplace.Replace(bson.ToJson(), (s) => s.Groups[1].Value);
        return Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(json);
    }
}

Be sure to reference Newtonsoft.Json.dll (http://james.newtonking.com/projects/json-net.aspx)

rrrr-o
  • 2,447
  • 2
  • 24
  • 52
  • 2
    Using `bson.ToJson(new JsonWriterSettings {OutputMode = JsonOutputMode.Strict})` removes the need for all the Regex processing to remove Mongo specific Json methods. – Andy McCluggage Jan 14 '15 at 16:42
14

Currently, there is no support for dynamic in the MongoDB driver. This is because it is based on .NET 3.5. However, since a .NET 4.0 assembly can reference a .NET 3.5 assembly, it is possible for you to write a IBsonSerializationProvider and an IBsonSerializer in .NET 4.0 to support dynamics.

We, 10gen, are looking at doing this in the future. I have spiked some support at https://github.com/craiggwilson/mongo-csharp-driver/tree/dynamic if you want to take a look. There are most definitely bugs, but it shows that it is possible.

Craig Wilson
  • 12,174
  • 3
  • 41
  • 45
  • Thanks Craig, i found a way to make it work. check here https://groups.google.com/forum/?fromgroups#!topic/mongodb-user/AEIfBP9IWQw – hoang Apr 20 '12 at 04:46
  • 1
    Yes, I saw that. I will echo here for SO users. That works fine, but requires a low level modification to the core. The way I presented above is a side-load and accomplishes the same thing without modifying the core. Obviously, there are some differences, but it gets most of the way there. It is just a spike after all, so... – Craig Wilson Apr 20 '12 at 16:34
  • 1
    Take a look at [MongoDB.Dynamic](http://www.assembla.com/spaces/mongodb-dynamic/wiki). It uses MongoDB official driver and take an approach of using interfaces with dynamics of C# 4.0. – Jone Polvora Jun 04 '12 at 20:49
  • Is this an official part of the driver now? – jjxtra Jul 10 '14 at 15:02
  • It will be in version 2.0, due out this year. – Craig Wilson Jul 10 '14 at 15:19
  • The link to https://github.com/craiggwilson/mongo-csharp-driver/tree/dynamic is no longer valid – slolife Mar 12 '15 at 16:25
  • @CraigWilson Would be good to have an update on this if possible please: the links are dead, it's 2015 and I can't find anything in the Mongo docs :) – Kieren Johnstone Apr 01 '15 at 09:55
  • 2
    We are still working on documentation, but we support dynamic in the 2.0.0 driver (currently in rc0): http://mongodb.github.io/mongo-csharp-driver/2.0/what_is_new/#new-api – Craig Wilson Apr 01 '15 at 11:43
5

I have a clean solution using custom IBsonSerializer and Newtonsoft.Json.

Setup your custom serializer on the BsonClassMap

map.MapProperty(member => member.Data)
   .SetElementName("Data")
   .SetSerializer(new DynamicSerializer());

or on the property

[BsonSerializer(typeof(DynamicSerializer))]
public dynamic Data { get; set; }

And just include the following class

public class DynamicSerializer : IBsonSerializer
{
  #region Implementation of IBsonSerializer

  public object Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options)
  {
    return Deserialize(bsonReader, nominalType, null, options);
  }

  public object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType,
    IBsonSerializationOptions options)
  {
    if (bsonReader.GetCurrentBsonType() != BsonType.Document) throw new Exception("Not document");
    var bsonDocument = BsonSerializer.Deserialize(bsonReader, typeof(BsonDocument), options) as BsonDocument;
    return JsonConvert.DeserializeObject<dynamic>(bsonDocument.ToJson());
  }

  public IBsonSerializationOptions GetDefaultSerializationOptions()
  {
    return new DocumentSerializationOptions();
  }

  public void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options)
  {
    var json = (value == null) ? "{}": JsonConvert.SerializeObject(value);
    BsonDocument document = BsonDocument.Parse(json);
    BsonSerializer.Serialize(bsonWriter, typeof(BsonDocument), document,options); 
  }

  #endregion
}

InfoSlips - GlobalKinetic

Rolf Wessels
  • 797
  • 9
  • 12
4

Just to build on Maximilian's answer. This will return a list of dynamics from any query.

    /// <summary>
    /// deserializes this BsonDocument cursor result to a list of .net dynamic objects
    /// </summary>
    /// <param name="cursor">cursor result to convert to dynamic</param>
    /// <returns></returns>
    public static List<dynamic> ToDynamicList(this MongoCursor<BsonDocument> cursor)
    {
        var dynamicList = new List<dynamic>();
        var list = cursor.ToList();
        for (int i = 0, l = list.Count; i < l; i++)
            dynamicList.Add(list[i].ToDynamic());

        return dynamicList;
    }
Donny V.
  • 22,248
  • 13
  • 65
  • 79
  • 3
    This seems redundant. You could just do `cursor.Select(doc => doc.ToDynamic())`. Add `.ToList()` if needed. In general, writing methods for which the framework already provides a good solution should be avoided. – Tomas Mar 25 '13 at 15:23
0

Even though this is an old topic its still just as relevant today as when post was made, and I have yet to see any solutions out there that provides a simple solution for two way support, I have modified @Maximilian Scherer code so that it lets you convert to dynamic objects which easily let you save your Document again.

public static class MongoDynamic
{
    /// <summary>
    /// deserializes this bson doc to a .net dynamic object
    /// </summary>
    /// <param name="bson">bson doc to convert to dynamic</param>
    public static dynamic ToDynamic(this BsonDocument bson)
    {
        var json = bson.ToJson(new MongoDB.Bson.IO.JsonWriterSettings { OutputMode = JsonOutputMode.Strict });
        dynamic e =  Newtonsoft.Json.JsonConvert.DeserializeObject<ExpandoObject>(json);
        BsonValue id;
        if (bson.TryGetValue("_id", out id))
        {
            // Lets set _id again so that its possible to save document.
            e._id = new ObjectId(id.ToString());
        }
        return e;
    }
}

Example of usage:

// Get BsonDocument from db here
BsonDocument doc = ...

// Convert to dynamic.
var d = doc.ToDynamic();

// Lets add a none existant property.
d.Name = "test";

// Assuming you already have your collection set up
collection.Save(new BsonDocument(d));
Joachim
  • 360
  • 2
  • 12