Summary
I have a class hierarchy with base and derived classes in C#.
The BsonId is stored on the base class and the derived classes hold some additional data.
I have an additional Id field on the derived class used for other purposes, and I set up the BsonClassMap to map it to “Id”, so it shouldn’t conflict with the BsonId.
I want to run a pipeline to make a query on the “derived” collection, but the pipeline fails to render to Bson.
The issue may be that ExpressionToAggregationExpressionTranslator creates a new BsonClassMap with AutoMap() and does not use the registered BsonClassMaps.
MongoDB.Driver version 2.19.0
How to Reproduce
Code:
using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Driver; BsonClassMap.RegisterClassMap<Derived>(cm => { cm.AutoMap(); cm.UnmapProperty(x => x.Id); cm.MapProperty(x => x.Id).SetElementName(nameof(Derived.Id)); }).Freeze(); var pipeline = new EmptyPipelineDefinition<Derived>() .Project(x => new Derived { Id = x.Id, }); var rendered = pipeline.Render(BsonSerializer.SerializerRegistry.GetSerializer<Derived>(), BsonSerializer.SerializerRegistry); Console.WriteLine(rendered); public abstract class Base { [BsonId] [BsonElement("_id")] public ObjectId UniqueId { get; set; } } public class Derived : Base { [BsonElement("Id")] public string Id { get; set; } }
Exception:
MongoDB.Bson.BsonSerializationException: The property 'Id' of type 'Derived' cannot use element name '_id' because it is already being used by property 'UniqueId' of type 'Base'. at MongoDB.Bson.Serialization.BsonClassMap.Freeze() at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MemberInitExpressionToAggregationExpressionTranslator.Translate(TranslationContext context, MemberInitExpression expression) at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.ExpressionToAggregationExpressionTranslator.Translate(TranslationContext context, Expression expression) at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(TranslationContext context, LambdaExpression lambdaExpression, IBsonSerializer parameterSerializer, Boolean asRoot) at MongoDB.Driver.Linq.Linq3Implementation.LinqProviderAdapterV3.TranslateExpressionToProjection[TInput,TOutput](Expression`1 expression, IBsonSerializer`1 inputSerializer, IBsonSerializerRegistry serializerRegistry, ExpressionTranslationOptions translationOptions) at MongoDB.Driver.ProjectExpressionProjection`2.Render(IBsonSerializer`1 inputSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider) at MongoDB.Driver.PipelineStageDefinitionBuilder.<>c__DisplayClass39_0`2.<Project>b__0(IBsonSerializer`1 s, IBsonSerializerRegistry sr, LinqProvider linqProvider) at MongoDB.Driver.DelegatedPipelineStageDefinition`2.Render(IBsonSerializer`1 inputSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider) at MongoDB.Driver.AppendedStagePipelineDefinition`3.Render(IBsonSerializer`1 inputSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider) at MongoDB.Driver.PipelineDefinition`2.Render(IBsonSerializer`1 inputSerializer, IBsonSerializerRegistry serializerRegistry) at Program.<Main>$(String[] args) in C:\src\MongoTest\MongoTest\Program.cs:line 19
I tried it out with different property names that do not cause conflict, but I got strange results:
the translator does not use the BsonClassMap for the target property in the assignment, only for the source.
Code:
using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Driver; BsonClassMap.RegisterClassMap<Derived>(cm => { cm.AutoMap(); cm.UnmapProperty(x => x.Id2); cm.MapProperty(x => x.Id2).SetElementName("xyz"); }).Freeze(); var pipeline = new EmptyPipelineDefinition<Derived>() .Project(x => new Derived { Id2 = x.Id2, }); var rendered = pipeline.Render(BsonSerializer.SerializerRegistry.GetSerializer<Derived>(), BsonSerializer.SerializerRegistry); foreach (var item in rendered.Documents) Console.WriteLine(item); public abstract class Base { [BsonId] [BsonElement("_id")] public ObjectId UniqueId { get; set; } } public class Derived : Base { [BsonElement("abc")] public string Id2 { get; set; } }
Result:
{ "$project" : { "abc" : "$xyz", "_id" : 0 } }
- related to
-
CSHARP-4819 ReplaceWith not respecting custom element name configured by the serializer
- Investigating