4

Is there a way to prevent a $type property being added when I save my dynamic type values?

When I save this:

new Activity {
    Name = "FormFieldDeleted",
    Body =  new {
        MyDeletedFormField(),
        MyCompleteForm()
    }
}

I get this

<>f__AnonymousType1`2[[MyProject.Domains.Forms.Models.FormField, MyProject.Domains.Forms],[MyProject.Domains.Forms.Entities.FormRegistration, MyProject.Domains.Forms]], MyProject.Api.Forms

But when I try to fetch this saved entity, it crashes with the exception below. I know it's missing a project reference, but I really don't want to add that reference (I don't want to reference an API from a console app). It's better for me to just prevent the $type property.

/usr/local/share/dotnet/dotnet path/MyProject/MyProject/src/MyProject.Tasks.MapActivities/bin/Debug/netcoreapp3.1/MyProject.Tasks.MapActivities.dll
Unhandled exception. System.InvalidOperationException: Could not convert document 31317d58-db9e-4f60-8dee-b8593f3e06c0 to entity of type MyProject.Domains.Core.Entities.Activity
 ---> Newtonsoft.Json.JsonSerializationException: Error resolving type specified in JSON '<>f__AnonymousType1`2[[MyProject.Domains.Forms.Models.FormField, MyProject.Domains.Forms],[MyProject.Domains.Forms.Entities.FormRegistration, MyProject.Domains.Forms]], MyProject.Api.Forms'. Path 'Body.$type'.
 ---> Newtonsoft.Json.JsonSerializationException: Could not load assembly 'MyProject.Api.Forms'
....
Tom Aalbers
  • 4,574
  • 5
  • 29
  • 51

2 Answers2

5

Yes, there is a way.
You can customize the way serialization works using the following code:

store.Conventions.CustomizeJsonSerializer = serializer =>
{
   serializer.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.None;
};

As an example, take a look at the code here : https://dotnetfiddle.net/voJ7US

If you execute the code at the dotnetfiddle, you can see the results here: http://live-test.ravendb.net/studio/index.html#databases/documents?collection=Activities&database=UniqueTestDB

Michael
  • 860
  • 7
  • 19
  • Ah great! Thank you for the solution. Do you know if this has any drawbacks? – Tom Aalbers May 03 '20 at 15:45
  • 1
    As far as I know, "None" is usually used - other settings are a security code smell for data received externally. See https://www.alphabot.com/security/blog/2017/net/How-to-configure-Json.NET-to-create-a-vulnerable-web-API.html – Michael May 04 '20 at 07:53
  • 1
    Note that with RavenDB 4.x $type is not used at all on the server-side, since on the server, json is deserialized with custom json serializer and not Newtonsoft.Json (see https://github.com/ravendb/ravendb/blob/v4.2/src/Sparrow/Json/BlittableJsonReaderObject.cs) – Michael May 04 '20 at 08:02
  • Thank you for the feedback, appreciate it! – Tom Aalbers May 04 '20 at 10:46
  • Saving the type is useful when you have inheritance, otherwise Newtonsoft will not be able to deserialize your types automatically. – Yuri Cardoso Jun 03 '20 at 06:16
1

For RavenDB 5 and higher it changed a bit.

var store = DocumentStore
{
    Urls = new[] { "your-endpoint" },
    Conventions = new DocumentConventions
    {
        Serialization = new NewtonsoftJsonSerializationConventions
        {
            CustomizeJsonSerializer = serializer =>
            {
                serializer.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.None;
            }
        }
    }
}.Initialize();

See https://ravendb.net/docs/article-page/5.0/file-header/migration/client-api/conventions for more information.

Tom Aalbers
  • 4,574
  • 5
  • 29
  • 51