NancyFx / Nancy.Serialization.JsonNet

NewtonSoft.Json serializer for Nancy
MIT License
40 stars 32 forks source link

Wrong order of serializers on some machines #36

Closed xmedeko closed 7 years ago

xmedeko commented 8 years ago

I have .NET 4.6.1, Nancy 1.4.3, Nancy.Serialization.JsonNet 1.4.1 and compile my project by VS 2015 C# 6.0. I use FormatterExtensions.AsJson to format the response. On some machines (I would call them MachinesA) the formatter.Serializers is

Nancy.Responses.DefaultXmlSerializer,Nancy.Serialization.JsonNet.JsonNetSerializer,Nancy.Responses.DefaultJsonSerializer

while on the other machines (MachinesB) the order is:

Nancy.Responses.DefaultXmlSerializer,Nancy.Responses.DefaultJsonSerializer,Nancy.Serialization.JsonNet.JsonNetSerializer

So the response is formatted by DefaultJsonSerializer and not JsonNetSerializer. I cannot find, what's the difference between MachinesA and MachinesB. They all have .NET 4.6.1 installed, they have various OS (Win 10, Win 7, ...). My application is always the same - just copy of the folder.

khellang commented 8 years ago

You have all the same versions? The only difference is if you switch from 4.x to 4.6.1? Sounds weird...

xmedeko commented 8 years ago

Yes, all the same versions of .NET 4.6.1. No, there is no switch from any other .NET version.

khellang commented 8 years ago

You mean you haven't tested with a different version of .NET? It sounded like this was something that broke when switching to 4.6.1...

xmedeko commented 8 years ago

I also use LightInjectNancyBootstrapper, may it be connected?

khellang commented 8 years ago

Yes, that may very well be connected. It's weird that it happens on only some machines, though :confused:

xmedeko commented 8 years ago

When I use the code from NancyInternalConfiguration:

AppDomainAssemblyTypeScanner.TypesOf<ISerializer>(ScanMode.ExcludeNancy).Union(new List<Type>(new[] { typeof(Nancy.Responses.DefaultJsonSerializer), typeof(Nancy.Responses.DefaultXmlSerializer) })).ToList())

The I got the list:

JsonNetSerializer,DefaultJsonSerializer,DefaultXmlSerializer

So which code shuffles this list so as the formatter has

DefaultXmlSerializer,JsonNetSerializer,DefaultJsonSerializer,

?

khellang commented 8 years ago

Yes, the list you want is the first one, so that's correct. This is then passed to the bootstrapper, which registers the types in the container. My guess is that it's the container that shuffles things around.

khellang commented 8 years ago

FYI; for the Autofac bootstrapper, we use a method called PreserveExistingDefaults (see this) to make sure the registrations stay in the "correct" order. Maybe this is where the LightInjectNancyBootstrapper fails?

xmedeko commented 8 years ago

Hmm, IMHO it's not good to rely on the container order. If it uses HashSet then the order is unpredictable - then you should register IEnumerable<IService> with the container to be sure to get the same order or something like that.

I'll try to check if LightInject has something similar to Autofac later.

khellang commented 8 years ago

Hmm, IMHO it's not good to rely on the container order.

Well, it's one way to ensure the custom serializers are picked over the default ones. If you have a better way that would work across all bootstrappers, please let us know :wink:

xmedeko commented 8 years ago

Yep, there are several possible ways: register whole CollectionTypeRegistration not the IService independently, or mark default with some IDefault interface, or mark default with some DefaultAttribute.

khellang commented 8 years ago

register whole CollectionTypeRegistration not the IService independently

What does this mean?

or mark default with some IDefault interface, or mark default with some DefaultAttribute

This is definitely possible, but how do you propose we use that interface or attribute?

xmedeko commented 8 years ago

About IDefault and DefaultAttribute: you can make the same logic as with DefaultStatusCodeHandler.

xmedeko commented 8 years ago

The problems occurs on the 64bit machines, on 32bit machines it works. The workaround is:

protected override NancyInternalConfiguration InternalConfiguration
{
    get
    {
        return NancyInternalConfiguration.WithOverrides(config =>
        {
            config.Serializers = new[] { typeof(DefaultXmlSerializer), typeof(JsonNetSerializer) };
        });
    }
}

This problem and the workaround should be documented, since it's hard to find.

thecodejunkie commented 7 years ago

All auto discoveries in Nancy are by nature not deterministic in order. It comes down to the order that the .NET type system picks things up. If you depend on the other, be explicit in the registration as shown by @xmedeko in this comment https://github.com/NancyFx/Nancy.Serialization.JsonNet/issues/36#issuecomment-206822301

xmedeko commented 7 years ago

@thecodejunkie what's the reason for closing this bug? The bug is in the NancyFX code, the FormatterExtensions.AsJson is not working well together with Nancy.Serialization.JsonNet. Is there any change in the NancyFx code, which fixed that?