2

Unable to cast object of type 'MongoDB.Bson.Serialization.Serializers.BsonValueSerializer' to type 'MongoDB.Bson.Serialization.IBsonSerializer'

Trying to perform a Pull from a list of sub-documents in MongoDB using the C# Driver (either 2.2.4 or 2.3.0).

This is how I do the update:

FilterDefinitionBuilder<Event> filter = new FilterDefinitionBuilder<Event>();
UpdateDefinitionBuilder<Event> update = new UpdateDefinitionBuilder<Event>();
_eventRepo.FindAndUpdate(filter.Eq("EventId", eventid), 
update.PullFilter("Documents", filter.Eq("DocId", docid)));

The called repository method:

public void FindAndUpdate(FilterDefinition<T> filter, UpdateDefinition<T> update)
{
    _context.Collection<T>().FindOneAndUpdate(filter, update);
}

This is what the MongoDB document looks like:

{
  "_id" : ObjectId("5825f74919c55e0c9c4727ee"), 
  "EventId" : "1234-5789",
  "Documents" : [
     {
        "DocId" : "07c03673-c572-4f56-aaad-0edb52b3a06c", 
        "Name" : "test.pdf"
     }
  ]
}

And this is the exception I get:

An exception of type 'System.InvalidCastException' occurred in MongoDB.Driver.dll but was not handled in user code
Additional information: Unable to cast object of type 'MongoDB.Bson.Serialization.Serializers.BsonValueSerializer' to type 'MongoDB.Bson.Serialization.IBsonSerializer`1[MongoDB.Bson.BsonDocument]'.

Stack trace:
at MongoDB.Driver.PullUpdateDefinition`2.Render(IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry)
at MongoDB.Driver.MongoCollectionImpl`1.CreateFindOneAndUpdateOperation[TProjection](FilterDefinition`1 filter, UpdateDefinition`1 update, FindOneAndUpdateOptions`2 options)
at MongoDB.Driver.MongoCollectionImpl`1.FindOneAndUpdate[TProjection](FilterDefinition`1 filter, UpdateDefinition`1 update, FindOneAndUpdateOptions`2 options, CancellationToken cancellationToken)

The exception doesn't make any sense because BsonValueSerializer implements / inherits IBsonSerializer (Documentation), so I would expect the Render should be able to take a BsonValueSerializer.

Obviously this is inside the FindOneAndReplace method of the C# BSon driver. Is this a bug of the driver or am I doing something wrong?

I found people that do the Pull the same way (here) and it seems to work for them. I have found what looks like a very similar problem (here), but the solution and discussion couldn't help me fix mine.

Community
  • 1
  • 1
jonlop
  • 47
  • 1
  • 5

1 Answers1

1

Filters are typed and you must match up the filters to what they are filtering.

Your filter on the collection should be a FilterDefinition<Event>.

Your filter used with PullFilter should be a FilterDefinition<Document>.

This isn't caught at compile time because you used "DocId" to identify the field and so there is no type information associated with the field. The type of the field was inferred from the filter passed to PullFilter, which is a filter on Event.

I recommend using the typed versions of the Filter and Update builders. Here's your code rewritten using the typed versions which results in a compile time error:

var filter = Builders<Event>.Filter.Eq(x => x.EventId, "abc");
var update = Builders<Event>.Update.PullFilter(x => x.Documents, Builders<Event>.Filter.Eq(x => x.DocId, "abc"));
collection.FindOneAndUpdate(filter, update);

Here's the code using Document instead of Event in the last usage of Builders:

var filter = Builders<Event>.Filter.Eq(x => x.EventId, "abc");
var update = Builders<Event>.Update.PullFilter(x => x.Documents, Builders<Document>.Filter.Eq(x => x.DocId, "abc"));
collection.FindOneAndUpdate(filter, update);
Robert Stam
  • 12,039
  • 2
  • 39
  • 36