Dynamic persistence with MongoDB - look, no classes! Multiple inheritance in C#!
Dynamic persistence with MongoDB - look, no classes! Multiple inheritance in C#!
In an earlier post I explained a technique to create a class-free persistence layer using MongoDB. [Read that post first, then come back here.]
Since then I've refined the techniques involved and created a cleaner implementation that does away with the `.props` collection on each object. Now when you add an interface to an object you get exactly what you expected in the persisted data.
To use it you first need to register the serialization code somewhere in your startup code...
BsonSerializer.RegisterSerializationProvider(new MongoDynamicSerializationProvider());
The Serialization provider is quite simple:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MongoDB.Bson.Serialization;
namespace MongoData.Dynamic
{
public class MongoDynamicSerializationProvider : IBsonSerializationProvider
{
public IBsonSerializer GetSerializer(Type type)
{
if (typeof(MongoDynamic).IsAssignableFrom(type)) return MongoDynamicBsonSerializer.Instance;
return null;
}
}
}
The serializer is a bit more involved. It uses an interface map to decide what type to return for each serialized object. This is critical because many different .NET types can map onto the same BSon serialized value and only by maintaining this map can we get back to the original type. It's also critical for handling nested object graphs containing different types.
And finally, the actual MongoDynamic class:
You'll need Impromptu interface (from Nuget) to build this. To use it, you write code like this to save to MongoDB:
MongoDynamic entity = new MongoDynamic();
var user = entity.AddLike\<IUser\>(); // Add the IUser fields to it ...
user.Name = name; // Use it as if it were an IUser
// save it to the database as normal
And to retrieve an object you create a query as normal and then query for MongoDynamic objects like so ...
var user = database.GetCollection\<MongoDynamic\>("\*\*\*collectionName\*\*\*").FindOne(query);
if (user == null) return null; return user.AsLike\<IUser\>();
Typically you will want your query to reference the field called int (where all the interfaces are stored) so you can query for objects that support a specific type (if you do, you'll want to add an index on that field). [NB the name was chosen to be one you were unlikely to ever use in .NET]
MongoDynamic objects are polymorphic - you can morph them to support any other interface at any time like so ...
user.AddLike\<ISomeOtherInterface\>();