1

I have a REST service for my game and decided to give MongoDB a try - seems pretty straight forward except for the "_id" field requirement.

The problem here is that my client app is using the REST services and has no knowledge of "mongodb", its drivers or dependencies - nor should it. To decouple the client from the server side (REST service provider) I need to get around the fact that MongoDB appears to require a "_id" field of type BsonObjectId.

Currently I'm using a lightweight DAO layer, so instead of having:

using System;

public class Item {
     private BsonObjectId _id;
     private string name;
}

I am using a DAO to translate this to something "mongodb agnostic":

using System;

public class ItemDAO {
     private string _id;
     private string name;
}

Ideally it would be nice to be rid of BsonObjectId entirely - is there some annotation/custom serialization handler that can be used or some way that I'm able to use a string instead of BsonObjectId?

Failing that, any way to get objects wrapped by MongoDB so they are decorated with the _id which I can inject back into the row as a string?

The ideal result would be to have no DAO class at all just "Item" and have Item using a string for _id (or something that does not require mongodb dependencies to bleed into client implementation).

Lypheus
  • 465
  • 4
  • 10

5 Answers5

5

Your documents must have an _id field, but it doesn't have to be an ObjectID. The only requirement is that it is unique for the collection.

MongoDB will generate an ObjectId for you if you don't supply an _id field when saving a new document, but that is just a helper function.

Martin
  • 5,119
  • 3
  • 18
  • 16
  • A lot of great help - thanks everyone! This is the answer for me, since this mongodb instance will be backing a relatively low volume data concern (game matchmaking server registration). – Lypheus Feb 12 '15 at 21:45
2

If you don't want to "polute" your model clases, you could register appropriate Id generator in you data access code.

BsonSerializer.RegisterIdGenerator(typeof(string), new StringObjectIdGenerator());

This way you will have String field in your model, but underneath it will be ObjectId, which is kind of nice i.e. you can see when the records where added (approx)

If you decide however that in your REST service you will accept Ids from clients (via PUT) then ObjectId is obviously not the way to go.

Have a look at this article since it describes how to setup serialization options etc.

marcinn
  • 1,786
  • 11
  • 13
0

How do you query for the objects? If you don't want / need the _id field in the client, you can use a projection to exclude the _id field from the result.

Also, be aware that generating your own string-based _id's can have severe impact on database size. The ObjectId seems to be a pretty efficient structure. I have experimented with using strings for _id, to avoid having an index on a field I would never use - but in my case the cost in database size made it unfeasible. Depends on your database size and the rest of the contents, of course.

Malakim
  • 1,333
  • 2
  • 18
  • 34
0

It's commonplace to represent BSON ObjectIds as strings; by default, Mongo drivers will generate 96-bit IDs, which you can then obviously represent as 24 hex bytes. Most client libraries have facilities for creating ObjectIds out of strings and casting them to strings.

You would have your external interfaces treat _id as a string, and then when your Mongo-aware DB layer receives an _id as a string, it would just internally convert it with ObjectId.from_string(_id) or whatnot. When writing results, you would just cast the OID to a string.

Chris Heald
  • 61,439
  • 10
  • 123
  • 137
0

Using ObjectIds as data type for your primary keys makes a lot of sense for various reasons. Generating good, non-sequential, monotonic IDs with low collision probability isn't trivial, and re-inventing the wheel or essentially rewriting the feature isn't worth the trouble.

Data mapping should be done in controllers, and they should interact with the outside using DTOs. In other words, your REST Endpoints (Controllers/Modules) should known DTOs while your database uses your database models.

The DTOs will look very similar to your models, but they might have a few less fields (neat if there's internal data you don't want exposed via the API) and they use strings where the models use ObjectIds.

To avoid stupid copying code, use something like AutoMapper.

Community
  • 1
  • 1
mnemosyn
  • 45,391
  • 6
  • 76
  • 82