Uploaded image for project: 'C# Driver'
  1. C# Driver
  2. CSHARP-4579

Pipeline projection translator does not use registered BsonClassMap and throws exception

    • Type: Icon: Bug Bug
    • Resolution: Works as Designed
    • Priority: Icon: Unknown Unknown
    • None
    • Affects Version/s: None
    • Component/s: Linq, LINQ3
    • Hide

      1. What would you like to communicate to the user about this feature?
      2. Would you like the user to see examples of the syntax and/or executable code and its output?
      3. Which versions of the driver/connector does this apply to?

      Show
      1. What would you like to communicate to the user about this feature? 2. Would you like the user to see examples of the syntax and/or executable code and its output? 3. Which versions of the driver/connector does this apply to?

      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.

       

      https://github.com/mongodb/mongo-csharp-driver/blob/master/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberInitExpressionToAggregationExpressionTranslator.cs#L31

       

      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 } } 

       

            Assignee:
            oleksandr.poliakov@mongodb.com Oleksandr Poliakov
            Reporter:
            fussatok@gmail.com Peter Ofella
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: