DNN Sharp modules (and most probably others too) rely on Newtonsoft dll to run properly and the Newtonsoft 7.0 version that was available in older DNN versions was not enough. This is the reason why we added our own assembly binding in the web.config file so that we know for sure that the needed libraries are being deployed and correctly referenced so that the modules will properly function.
Along with the DNN 9.2.0 update, when the Newtonsoft library was also updated, a lot of our clients experienced problems upgrading the DNN version which resulted in a Newtonsoft.json.dll error.
The problem was discussed and also a solution was offered but we're not sure it will be included in DNN 9.2.2 (but rather in DNN 9.3.0).
In order to get things working for you we've prepared a standalone installable package that will get you a successful DNN 9.2.0, 9.2.1 or even 9.2.2 RC upgrade process.
All you need to do is to install the package here before starting the DNN Upgrade process.
We've also submitted the solution that should close issue #2121 and details can be found here. For all those of you that need a little more code to digest with this article then go ahead and continue reading...
Our devs debugged through the upgrade process of the Newtonsoft library and here are the findings:
The DNN manifest file has two components (in this order):
1. Assembly component
2. Config component
The installer executes the Assembly component installer that copies the newtonsoft.json.dll version 10.0.3 in bin folder and then calls [`AddOrUpdateBindingRedirect()`](https://github.com/dnnsoftware/Dnn.Platform/blob/development/DNN%20Platform/Library/Services/Installer/Installers/AssemblyInstaller.cs#L194) which updates the `web.config` with the binding redirect to 10.0.3.
Then, the Config installer is executed which updates the `web.config` with the assembly binding once more.
The issue with DnnSharp's `<codebase>` assembly binding (and maybe other vendors) is in that second step of the install process. The [`path`](https://github.com/dnnsoftware/Dnn.Platform/blob/1829d1810e6ca6476302669060717c3c9b1f2d86/DNN%20Platform/Components/Newtonsoft/DotNetNuke.Newtonsoft.Json.dnn#L33) attribute set in the Newtonsoft package to update the binding redirect is not specific enough and doesn't take into consideration that there could be other `dependentAssembly` elements in `web.config` for newtonsoft. It targets the first `ab:dependentAssembly` element with `@name='Newtonsoft.Json'`.
In a clean DNN pre 9.2.0 the `<assemblyBinding>` element contains only one `dependentAssembly`:
``` XML
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
```
When installing any DnnSharp module the `assemblyBinding` changes to:
``` XML
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
...
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" />
<codeBase version="8.0.0.0" href="bin\NewtonSoft.Json-8.0.0.0\Newtonsoft.Json.dll" />
</dependentAssembly>
```
After the AssemblyInstaller is executed the `web.config` changes to (note the order of the elements):
``` XML
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" />
<codeBase version="8.0.0.0" href="bin\NewtonSoft.Json-8.0.0.0\Newtonsoft.Json.dll" />
</dependentAssembly>
...
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" />
<bindingRedirect oldVersion="0.0.0.0-32767.32767.32767.32767" newVersion="10.0.0.0" />
</dependentAssembly>
```
Now it is ConfigInstaller turn to update the `web.config`. It [searches](https://github.com/dnnsoftware/Dnn.Platform/blob/development/DNN%20Platform/Library/Services/Installer/XmlMerge.cs#L250) for the dependentAssembly with path `/configuration/runtime/ab:assemblyBinding/ab:dependentAssembly[ab:assemblyIdentity/@name='Newtonsoft.Json']` which matches the first one (DnnSharp's one). Then it [searches](https://github.com/dnnsoftware/Dnn.Platform/blob/development/DNN%20Platform/Library/Services/Installer/XmlMerge.cs#L531) for the target node with `/configuration/runtime/ab:assemblyBinding/ab:dependentAssembly[ab:assemblyIdentity/@name='Newtonsoft.Json']/ab:bindingRedirect` which matches the `bindingRedirect` of the second `dependentAssembly` (the one added earlier by AssemblyInstaller). Next it tries to [remove](https://github.com/dnnsoftware/Dnn.Platform/blob/development/DNN%20Platform/Library/Services/Installer/XmlMerge.cs#L561) the `bindingRedirect` of the second dependentAssembly from the first dependentAssembly. This throws a `System.ArgumentException` with message `The node to be removed is not a child of this node.`
### Solution
There are two solutions:
1. Strengthen the path's XPath like this: `/configuration/runtime/ab:assemblyBinding/ab:dependentAssembly[ab:assemblyIdentity/@name='Newtonsoft.Json' and ab:bindingRedirect]`. This will prevent mismatching rootNode / targetNode elements between codabase and bindingRedirect bindings. There could be other bindingRedirect bindings that could interfere with this.
2. Add an additional Config component before the existing one that removes all the bindingRedirects for older newtonsoft assemblies **but not the codabase bindings**. Then, the existing Config component will insert a bindingRedirect only if there is no bindingRedirect for version 10. There could be unforeseen issues with this approach.