Reputation: 25983
I'm having big problems when I'm overriding the Id
property in a derived class in my MongoDB storage setup.
The base class that all of my data models inherit looks like this:
public abstract class DataModel
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public virtual string Id { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
public DateTime Deleted { get; set; }
}
Then there are a few particular child data models that use upserts. This requires me to annotate the overridden Id
property with [BsonIgnoreIfDefault]
as per this answer:
public class ChildDataModel : DataModel
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
[BsonIgnoreIfDefault] // <---- need this for Upserts to work
public override string Id { get; set; }
... // child-specific properties
}
But unfortunately it results in this error:
The property 'Id' of type 'Namespace.ChildDataModel' cannot use element name '_id' because it is already being used by property 'Id' of type 'Namespace.DataModel'.
I've tried registering class maps, adding a type discriminator with RootClass = true
on the base model, as well as defining my child data models on the base class with the special [BsonKnownTypes(typeof(ChildDataModel), ...)]
attribute, but to no avail.
What am I doing wrong here?
Upvotes: 5
Views: 2690
Reputation: 49975
MongoDB driver by convention tries to map all the properties named Id
into _id
in class map. As you have two classes it registers the _id
twice. What's more BsonIgnoreIfDefault
would work fine if the Id
was null
but here it's not since the driver automatically will generate the value when you insert new document.
To fix that you can use BsonIgnore
if you want to have single _id
in MongoDB
public class ChildDataModel : DataModel
{
[BsonRepresentation(BsonType.ObjectId)]
[BsonIgnore]
public override string Id { get; set; }
}
will be stored as:
{
"_id" : ObjectId("5cb5fe72e2a22b3848b6a1f6"),
"Created" : ISODate("2019-04-16T16:10:25.908Z"),
"Modified" : ISODate("2019-04-16T16:10:25.914Z"),
"Deleted" : ISODate("2019-04-16T16:10:25.914Z")
}
or you can use BsonNoId
attribute if you want to have two values stored separately:
[BsonNoId]
public class ChildDataModel : DataModel
{
[BsonRepresentation(BsonType.ObjectId)]
public override string Id { get; set; }
}
will be:
{
"_id" : ObjectId("5cb5fecde2a22b3088ef731c"),
"Created" : ISODate("2019-04-16T16:11:56.810Z"),
"Modified" : ISODate("2019-04-16T16:11:56.822Z"),
"Deleted" : ISODate("2019-04-16T16:11:56.822Z"),
"Id" : ObjectId("5cb5fecde2a22b3088ef731c")
}
however it's still the same value from application point of view
Upvotes: 5