Alexander Kahoun
Alexander Kahoun

Reputation: 2488

Embedded MongoDB Document not getting an ID on save with C# Driver

When I have a root document that has a list of documents and I save/insert, the root document always gets an ID from MongoDB, but the documents in the list don't, they remain the same as ObjectId.Empty. I'm not sure if I'm doing something wrong or if this is intended behavior?

Here's an example of what I'm trying to do: Given the classes:

public class Foo
{
    public ObjectId Id { get; set; }
    public string Name { get; set; }
    public IList<Bar> Bars { get; set; }
}

public class Bar
{
    public ObjectId Id { get; set; }
    public string Name { get; set; }
}

With a Factory:

public class Factory
{
    public Foo CreateFoo(string name)
    {
        return new Foo {
            Id = new ObjectId(),
            Name = name,
            Bars = new List<Bar>()
        };
    }

    public Bar CreateBar(string name)
    {
        return new Bar {
            Id = new ObjectId(),
            Name = name
        };
    }
}

When I run a sample like this:

public class Program
{
    static void Main(string[] args)
    {
        var client = new MongoClient("mongodb://localhost");
        var server = client.GetServer();
        var database = server.GetDatabase("Example1");
        if (!database.CollectionExists("foos")) database.CreateCollection("foos");

        var factory = new Factory();
        var foo1 = factory.CreateFoo("first");
        foo1.Bars.Add(factory.CreateBar("bar"));
        var collection = database.GetCollection("foos");
        collection.Insert(foo1);

        var foo2 = factory.CreateFoo("second");
        collection.Insert(foo2);
        foo2.Bars.Add(factory.CreateBar("bar"));
        collection.Save(foo2);
    }
}

Both foo1 and foo2 get _id's generated by MongoDB, but none of the Bars do. All the Bars still have the ObjectId.Empty. Now an easy solution is to update the factory to use ObjectId.GenerateNewId(), but I'd feel more comfortable with it being generated from the DB. Does anyone have any insight on this? Am I doing something wrong? Is this even possible? Thanks for the input.

Upvotes: 4

Views: 4364

Answers (3)

user919532
user919532

Reputation:

There is one frequent scenario to update or remove one element of the embeded document collection. The only way is to rely on the id.

At the moment I have built a customised IIdGenerator and have assigned it to the IdMemberMap, but it only gets called for the root document.

Is there any other way to generate automatic ids for embeded documents?

Upvotes: 0

Zaid Masud
Zaid Masud

Reputation: 13463

The driver will only generate the Id field for you when the fields is identified as the _id of a collection. In your case, Bar is embedded inside the Foo document.

Therefore, it doesn't make sense for Bar to have an auto-generated Id field.

In your example, there is really no reason for Bar to have a unique _id. When you query the collection, you will query based on the _id of Foo and you'll have Bar objects available in the response.

This is definitely intended behavior.

Upvotes: 1

sambomartin
sambomartin

Reputation: 6813

It's intended behaviour. Each document (not sub-document) has an _id which get assigned automatically if empty.

You either need to have a separate collection of Bars and reference it from the Foo documents, or manually generate the ObjectIds.

If you're doing it from a factory [ or repository ] then it would be solid.

EDIT:

Also, if you use

[BsonRepresentation(BsonType.ObjectId)]
string Id {get;set;}

It means you can access the objectId value more easily, it would still be saved of ObjectId.

Upvotes: 1

Related Questions