[CSHARP-4532] We're no longer able to use dynamic as property types Created: 17/Feb/23  Updated: 02/Mar/23  Resolved: 02/Mar/23

Status: Closed
Project: C# Driver
Component/s: Serialization
Affects Version/s: 2.19.0
Fix Version/s: None

Type: Bug Priority: Unknown
Reporter: Erik Hennerfors Assignee: Boris Dogadov
Resolution: Duplicate Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
related to CSHARP-4534 Consider adding anonymous types to De... Closed
Documentation Changes Summary:

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?


 Description   

Hi

We're having problems with the `ObjectSerializer` for the MongoDB.Driver.Core driver version `2.19.0`.

We're storing "fingerprints" of objects where we use dynamic to save the properties used to calculate what we call a "fingerprint"-hash.

We have the following class
```c#
public class Fingerprint
{
[BsonElement("id"), BsonId, BsonIgnoreIfDefault, BsonRepresentation(BsonType.ObjectId)]
public string Id

{ get; set; }
[BsonElement("createdAt"), BsonDateTimeOptions(Kind = DateTimeKind.Utc)]
public DateTime CreatedAt { get; set; }

= DateTime.Now;
[BsonElement("hash")]
public string Hash

{ get; set; }
[BsonElement("data")]
public object Data { get; set; }

[BsonElement("metadata")]
public object Metadata

{ get; set; }

}
```

We're then creating an instance of this object that populate our dynamic fields with C# base types (`int`, `long`, `boolean` etc.)

```c#
public Models.Fingerprint ToFingerprint => new Models.Fingerprint
{
Hash = Fingerprint,
Data = new
{
Data.UserId,
Data.Name,
Data.Type,
Data.Domain,
Data.SiteName,
Data.Role,
Camps = new

{ Count = CampsData.CampCount, NotInvoiced = CampsData.NotInvoicedCount }

,
Club = new

{ ClubData.Id, ClubData.Name, ClubData.AttributeCount, ClubData.AttributeCategoryCount, ClubData.GroupsInClubCount, ClubData.HasActivatedBilling, ClubData.HasActivatedCriminalRecordSettings, ClubData.HasActivatedLokIntegration, ClubData.HasActivatedMembershipCards, ClubData.HasClubLogo, ClubData.HasFacebookConnection, ClubData.KakserviceSaleCreatedAt, ClubData.MembershipInvoicesCount, ClubData.MissingCriminalRecordCount, ClubData.NewbodySaleCreatedAt, ClubData.PersonsCount, ClubData.YoucalAgreementStatus }

,
Package = new

{ Packages.Type, Packages.Package }

},
Metadata = new

{ Data.Id }

};
```

When using Newtonsoft we get the following result

```json
{
    "id": null,
    "createdAt": "2023-02-17T18:06:40.3767748+01:00",
    "hash": "E929580C21FD36E878CCA03E38D95C57CD1DB7507A55965DFD036C2F9BFFE2AF821C616485EDAA7CF68A48E66A153F9A688829E692F440C78B56F7377AC37F5E",
    "data": {
        "userId": 1024439,
        "name": "Eds FF",
        "type": 3,
        "domain": "laget.se",
        "siteName": "EdsFF",
        "role": 1,
        "camps":

{             "count": 0,             "notInvoiced": 0         }

,
        "club":

{             "id": 22633,             "name": "Eds FF",             "attributeCount": 0,             "attributeCategoryCount": 0,             "groupsInClubCount": 44,             "hasActivatedBilling": true,             "hasActivatedCriminalRecordSettings": false,             "hasActivatedLokIntegration": true,             "hasActivatedMembershipCards": true,             "hasClubLogo": true,             "hasFacebookConnection": true,             "kakserviceSaleCreatedAt": null,             "membershipInvoicesCount": 1878,             "missingCriminalRecordCount": 73,             "newbodySaleCreatedAt": null,             "personsCount": 1010,             "youcalAgreementStatus": 2         }

,
        "package": {
            "type": 0,
            "package":

{                 "siteId": 61958,                 "siteType": 3,                 "packageType": 3,                 "daysLeft": 4930             }

        }
    },
    "metadata":

{         "id": 61958     }

}
```

Why can't we longer use `dynamic` as a propety type?

Kind Regards
Erik



 Comments   
Comment by Boris Dogadov [ 02/Mar/23 ]

Thanks for reporting this erik.hennerfors@laget.se.

Follow up work will be done in CSHARP-4534.

Closing this ticket.

Comment by Erik Hennerfors [ 24/Feb/23 ]

Hi
We don't have any custom BsonSerializer but adding the following block to `Program.cs` and the Main method seems to be working.

public static void Main(string[] args)
{
    // 2.19 introduced a security fix (CSHARP-4475) that requires any object serialization to specify the allowed types for serialization.
    // For allowing the default system types and anonymous types, please add this code to application startup:
    var objectSerializer = new ObjectSerializer(type =>
        ObjectSerializer.DefaultAllowedTypes(type) ||
        type.FullName.StartsWith("<>f__AnonymousType"));
    BsonSerializer.RegisterSerializer(objectSerializer);
 
    CreateHostBuilder(args).Build().Run();
}

We'll dig into our dependencies to see if one of those is adding custom serializers!

Comment by Boris Dogadov [ 23/Feb/23 ]

Hi erik.hennerfors@laget.se 

Driver uses static serialization registry, and this exception indicates that ObjectSerializer is already registered.
That can happen automatically during some prior serialization operation which involves ObjectSerializer.

You need to ensure that 

BsonSerializer.RegisterSerializer(objectSerializer);

is executed in some initialization code before any other serialization setup/operations are executed.

Please note that there is also 

BsonSerializer.TyRegisterSerializer

 method, but if will throw exception is there is already other serializer registered with different settings (for example different lambda instance).

Comment by Erik Hennerfors [ 23/Feb/23 ]

@Boris Dogadov

We've tried adding the following

// 2.19 introduced a security fix (CSHARP-4475) that requires any object serialization to specify the allowed types for serialization.
// For allowing the default system types and anonymous types, please add this code to application startup:
var objectSerializer = new ObjectSerializer(type =>
    ObjectSerializer.DefaultAllowedTypes(type) ||
    type.FullName.StartsWith("<>f__AnonymousType"));
BsonSerializer.RegisterSerializer(objectSerializer);

But we're getting the following errors,

MongoDB.Bson.BsonSerializationException: 'There is already a serializer registered for type Object.'

Comment by Erik Hennerfors [ 23/Feb/23 ]

Hi Boris Dogadov
I see, thank you for the clarification and the example, we'll update our codebase and follow CSHARP-4534

Best regards
Erik

Comment by Boris Dogadov [ 18/Feb/23 ]

Hi erik.hennerfors@laget.se 

2.19 introduced a security fix (CSHARP-4475) that requires any object serialization to specify the allowed types for serialization.
For allowing the default system types and anonymous types, please add this code to application startup:

var objectSerializer = new ObjectSerializer(type => ObjectSerializer.DefaultAllowedTypes(type) || type.FullName.StartsWith("<>f__AnonymousType"));
BsonSerializer.RegisterSerializer(objectSerializer);

Please note that this is a quick and informal way to validate whether the type is anonymous . There are other ways to check whether a type is an anonymous one.

We'll be researching this further in CSHARP-4534
 

Comment by Erik Hennerfors [ 17/Feb/23 ]

Did not realise that I can't edit the issue after creating it, but we're getting the following exception

MongoDB.Bson.BsonSerializationException: An error occurred while serializing the Data property of class Intercom.Service.Models.Fingerprint: Type <>f__AnonymousType5` is not configured as an allowed type for this instance of ObjectSerializer.

Here is the json representation of the object we're trying to save fo mongo that is converted to json using Newtonsoft.

{
  "id": null,
  "createdAt": "2023-02-17T18:06:40.3767748+01:00",
  "hash": "E929580C21FD36E878CCA03E38D95C57CD1DB7507A55965DFD036C2F9BFFE2AF821C616485EDAA7CF68A48E66A153F9A688829E692F440C78B56F7377AC37F5E",
  "data": {
    "userId": 1024439,
    "name": "Eds FF",
    "type": 3,
    "domain": "laget.se",
    "siteName": "EdsFF",
    "role": 1,
    "camps": {
      "count": 0,
      "notInvoiced": 0
    },
    "club": {
      "id": 22633,
      "name": "Eds FF",
      "attributeCount": 0,
      "attributeCategoryCount": 0,
      "groupsInClubCount": 44,
      "hasActivatedBilling": true,
      "hasActivatedCriminalRecordSettings": false,
      "hasActivatedLokIntegration": true,
      "hasActivatedMembershipCards": true,
      "hasClubLogo": true,
      "hasFacebookConnection": true,
      "kakserviceSaleCreatedAt": null,
      "membershipInvoicesCount": 1878,
      "missingCriminalRecordCount": 73,
      "newbodySaleCreatedAt": null,
      "personsCount": 1010,
      "youcalAgreementStatus": 2
    },
    "package": {
      "type": 0,
      "package": {
        "siteId": 61958,
        "siteType": 3,
        "packageType": 3,
        "daysLeft": 4930
      }
    }
  },
  "metadata": {
    "id": 61958
  }
}

Generated at Wed Feb 07 21:48:31 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.