Using MongoDb _Id field with C# POCOs
When using official C# mongo driver any field with name ‘Id’ and type “ObjectId” is mapped to _Id field in mongo db.What that means is that If you don’t supply some value for “Id” field default value would be generated (using default Id Generator)and the same will be deserialized into Id field of your POCO while fetching the data.
Lets take a simple POCO class as shown below .
public class Post { public ObjectId Id { get; set; } public string Title { get; set; } public string Tags { get; set; } public string Description { get; set; } }
Saving this class without providing value for Id field would automatically generate the object id value as shown below.
But for most of the real world applications you cannot constrain yourself with the restriction of having type of Id field as “ObjectId” and name as “Id”.
In these cases we need to do few extra things to ensure that mapping and id generation happens properly.
Mapping a property to _Id field
We can use any property of our POCO class as Id property.Lets see what happens when I rename Id field to PostId.
public class Post { public ObjectId PostId { get; set; } public string Title { get; set; } public string Tags { get; set; } public string Description { get; set; } }
Trying to save this POCO we get following error “System.InvalidOperationException: No IdGenerator found.” To map this property we can use BsonId attribute as shown below.
public class Post { [BsonId] public ObjectId PostId { get; set; } public string Title { get; set; } public string Tags { get; set; } public string Description { get; set; } }
And it will map to _Id value correctly.
Changing type of Id Property
Lets say I want to have my Id field as a string i.e. I want to have Id saved as ObjectId in mongodb but should be string in my POCO class.If I try this with current code again I will get same error i.e. “System.InvalidOperationException: No IdGenerator found.” To make this work you need to pass a parameter to BsonId attribute as shown below.This specifies the IdGenerator to use which in our case is StringObjectIdGenerator.
public class Post { [BsonId(IdGenerator=typeof(StringObjectIdGenerator))] public string PostId { get; set; } public string Title { get; set; } public string Tags { get; set; } public string Description { get; set; } }
This is an inbuilt id generator which is used for string represented as ObjectId in MongoDb. MongoDb provides couple of inbuilt Id Generators as shown below.
- BsonObjectIdGenerator
- CombGuidGenerator
- GuidGenerator
- NullIdChecker
- ObjectIdGenerator
- StringObjectIdGenerator
- ZeroIdChecker<T>
For more details visit this link.
Custom Id Generator
Finally there may be situations where you want to generate your own ids.This can be accomplished by implementing a custom id generator.Let say we want to generate custom ids which will have following format <MyApplicationName>_<GUID>.
To create custom id generator we need a class that implement IIdGenerator interface and use that as Id Generator type.
Custom Id Generator:
public class BlogIdGenerator : IIdGenerator { public object GenerateId(object container, object document) { return "Blog_" + Guid.NewGuid().ToString(); } public bool IsEmpty(object id) { return id == null || String.IsNullOrEmpty(id.ToString()); } }
Using Id Generator in our POCO class:
public class Post { [BsonId(IdGenerator = typeof(BlogIdGenerator))] public string PostId { get; set; } public string Title { get; set; } public string Tags { get; set; } public string Description { get; set; } }
Results:
I had to struggle initially to understand how all these scenarios worked (may be because I didn’t pay enough attention to basics).Hope this post will help other to get started smoothly with these basic concepts.
Additional Resources
MongoDb official documentation
Pluralsight course on Using MongoDB with ASP.NET MVC
C# and .Net MongoDb driver
great post saved me a bunch of time. Took a while to find this great info. I feel its underdiscussed.
Thx
Thanks for information.
Do have to depend on mongo db driver for the Id property.
Is there a way just leave POCO classes without any references.
No need to reference db driver from an object being serialized to express which property is identity.
Just use BsonClassMap from your initialization code:
var classMap = new BsonClassMap(typeof (TModel))
.MapIdField(“Id”)
.SetIdGenerator(StringObjectIdGenerator.Instance)
.ClassMap;
BsonClassMap.RegisterClassMap(classMap);
You could also use generic version of the class to avoid having to write “Id” as a string.
Thank you for this. I wasn’t thrilled about adding MongoDB.Bson as a dependency on my otherwise “clean” shared domain library.
Thanks!
How will this work in case of concurrent inserts and say you are using sequential integers (1, 2, 3,…) for ID ???