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

Deserialization of polymorphic types throwing exception

    • Type: Icon: Bug Bug
    • Resolution: Works as Designed
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: 2.4.4
    • Component/s: Serialization
    • Environment:
      Windows 10

      Am trying to deserialize polymorphic classes. The default deserialization works when document is created in the same function, but throws an exception when trying to deserialize else where.

      Here are SIX steps to reproduce:
      1) Create a blank asp.net core 2 mvc webapi project.
      2 Add necessary nuget's for Mongo Driver

      3) In start up, add this line: BsonSerializer.RegisterDiscriminatorConvention(typeof(Base), new ScalarDiscriminatorConvention("_t"));

      Default template contains values controller:

      4) Replace the file with below code:

      ##################################################################################
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Threading.Tasks;
      using Microsoft.AspNetCore.Mvc;
      using MongoDB.Driver;
      using MongoDB.Bson.Serialization;
      using MongoDB.Bson.Serialization.Conventions;
      using MongoDB.Bson.Serialization.Attributes;
      using MongoDB.Bson.Serialization.IdGenerators;
      using MongoDB.Bson;

      namespace apitest.Controllers
      {
      [Route("api/[controller]")]
      public class ValuesController : Controller
      {
      // GET api/values
      [HttpGet]
      public IActionResult Get()
      {
      List<Base> objs = new List<Base>();
      Base b = new Base2()

      { baseName = "BaseName", IBaseName = "IBaseName", base2Name = "base2Name" }

      ;
      Base d = new Derived()

      { baseName = "BaseName", IBaseName = "IBaseName", derivedName = "derivedName" }

      ;
      objs.Add(b);
      objs.Add(d);

      MongoDbContext m = new MongoDbContext();
      var testCollection = m.GetCollection<Base>("test");

      testCollection.DeleteMany(_ => true);

      foreach (var obj in objs)

      { testCollection.InsertOne(obj); Console.WriteLine($"Completed write"); }

      var filterDefinition = Builders<Base>.Filter.Where(s => true);
      IEnumerable<Base> list = testCollection.Find(filterDefinition).ToList();

      foreach (var o in list)
      {
      Console.WriteLine($"

      {o.GetType().Name}

      ");
      }
      Console.ReadLine();
      return new OkResult();
      }
      }

      public abstract class Base
      {
      [BsonIgnoreIfDefault]
      [BsonId]
      [BsonRepresentation(BsonType.ObjectId)]
      public string Id;
      public string baseName;
      public string IBaseName

      { get; set; }

      }

      public class Base2 : Base

      { public string base2Name; }

      public class Derived : Base

      { public string derivedName; }

      public class MongoDbContext
      {
      private IMongoDatabase _db;
      public MongoDbContext()

      { var client = new MongoClient("mongodb://localhost:27017"); _db = client.GetDatabase("test"); }

      public IMongoCollection<T> GetCollection<T>(string collectionName)

      { return _db.GetCollection<T>(collectionName); }

      }

      }

      ##################################################################################

      Note that console properly shows the derived classes deserialized.

      5) Now, comment the following lines in the Get Method:
      ##################################################################################
      //testCollection.DeleteMany(_ => true);

      //foreach (var obj in objs)
      //

      { // testCollection.InsertOne(obj); // Console.WriteLine($"Completed write"); //}

      ##################################################################################

      6) Run again and see that it throws following exception.
      ##################################################################################
      Application started. Press Ctrl+C to shut down.
      fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[0]
      An unhandled exception has occurred while executing the request
      System.InvalidOperationException: Can't compile a NewExpression with a constructor declared on an abstract class
      at System.Linq.Expressions.Compiler.LambdaCompiler.EmitNewExpression(Expression expr)
      at System.Linq.Expressions.Compiler.LambdaCompiler.EmitExpression(Expression node, CompilationFlags flags)
      at System.Linq.Expressions.Compiler.LambdaCompiler.EmitLambdaBody(CompilerScope parent, Boolean inlined, CompilationFlags flags)
      at System.Linq.Expressions.Compiler.LambdaCompiler.EmitLambdaBody()
      at System.Linq.Expressions.Compiler.LambdaCompiler.Compile(LambdaExpression lambda)
      at System.Linq.Expressions.Expression`1.Compile(Boolean preferInterpretation)
      at MongoDB.Bson.Serialization.BsonClassMap.GetCreator()
      at MongoDB.Bson.Serialization.BsonClassMap.CreateInstance()
      at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.DeserializeClass(BsonDeserializationContext context)
      at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
      at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize[TValue](IBsonSerializer`1 serializer, BsonDeserializationContext context)
      at MongoDB.Driver.Core.Operations.CursorBatchDeserializationHelper.DeserializeBatch[TDocument](RawBsonArray batch, IBsonSerializer`1 documentSerializer, MessageEncoderSettings messageEncoderSettings)
      at MongoDB.Driver.Core.Operations.FindCommandOperation`1.CreateCursorBatch(BsonDocument commandResult)
      at MongoDB.Driver.Core.Operations.FindCommandOperation`1.CreateCursor(IChannelSourceHandle channelSource, BsonDocument commandResult, Boolean slaveOk)
      at MongoDB.Driver.Core.Operations.FindCommandOperation`1.Execute(IReadBinding binding, CancellationToken cancellationToken)
      at MongoDB.Driver.Core.Operations.FindOperation`1.Execute(IReadBinding binding, CancellationToken cancellationToken)
      at MongoDB.Driver.OperationExecutor.ExecuteReadOperation[TResult](IReadBinding binding, IReadOperation`1 operation, CancellationToken cancellationToken)
      at MongoDB.Driver.MongoCollectionImpl`1.ExecuteReadOperation[TResult](IReadOperation`1 operation, ReadPreference readPreference, CancellationToken cancellationToken)
      at MongoDB.Driver.MongoCollectionImpl`1.ExecuteReadOperation[TResult](IReadOperation`1 operation, CancellationToken cancellationToken)
      at MongoDB.Driver.MongoCollectionImpl`1.FindSync[TProjection](FilterDefinition`1 filter, FindOptions`2 options, CancellationToken cancellationToken)
      at MongoDB.Driver.FindFluent`2.ToCursor(CancellationToken cancellationToken)
      at MongoDB.Driver.IAsyncCursorSourceExtensions.ToList[TDocument](IAsyncCursorSource`1 source, CancellationToken cancellationToken)
      at apitest.Controllers.ValuesController.Get() in C:\ws\gh\temp\apitest\apitest\Controllers\ValuesController.cs:line 40
      at lambda_method(Closure , Object , Object[] )
      at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
      at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeActionMethodAsync>d__12.MoveNext()
      — End of stack trace from previous location where exception was thrown —
      at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
      at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
      at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeNextActionFilterAsync>d__10.MoveNext()
      — End of stack trace from previous location where exception was thrown —
      at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
      at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
      at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
      at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeInnerFilterAsync>d__14.MoveNext()
      — End of stack trace from previous location where exception was thrown —
      at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
      at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
      at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.<InvokeNextResourceFilter>d__22.MoveNext()
      — End of stack trace from previous location where exception was thrown —
      at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
      at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
      at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
      at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.<InvokeFilterPipelineAsync>d__17.MoveNext()
      — End of stack trace from previous location where exception was thrown —
      at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
      at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
      at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.<InvokeAsync>d__15.MoveNext()
      — End of stack trace from previous location where exception was thrown —
      at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
      at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
      at Microsoft.AspNetCore.Builder.RouterMiddleware.<Invoke>d__4.MoveNext()
      — End of stack trace from previous location where exception was thrown —
      at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
      at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
      at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.<Invoke>d__7.MoveNext()
      ##################################################################################

            Assignee:
            wan.bachtiar@mongodb.com Wan Bachtiar
            Reporter:
            kiranj kiranj email
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: