Newtonsoft.Json reference complaining on Azure Functions

18,358

Solution 1

@JustInChronicles, I'm adding this here as an answer for reference, but the expected behavior should be that indirect dependencies of private assemblies are resolved from your bin folder, as expected.

I put together the following test to reproduce your scenario:

  • Created a simple class library with a simple type that uses Json.NET to serialize an object and return the JSON string. This assembly references Json.NET 8.0.3. The result includes the Json.NET assembly version it is using
  • Created a function that references that type only with a #r "DependencyWithJsonRef.dll" and returns the result produced by the method mentioned above
  • Deployed DependencyWithJsonRef.dll and Newtonsoft.Json.dll (8.0.3) to my function's bin folder

Invoking the function produces the expected result.

Here is the function, for reference:

#r "DependencyWithJsonRef.dll"

using System.Net;

public static string Run(HttpRequestMessage req, TraceWriter log)
{
    var myType = new DependencyWithJsonRef.TestType();
    return myType.GetFromJson();
}

As you can see, no explicit reference to indirect dependencies (Json.NET) required.

This is the output I get:

{
    "Prop1":"Test",
    "Prop2":1,
    "AssemblyName": "Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed"
}

Quick note: One thing you may want to check, particularly if you've updated that dependency while developing your function is that assembly resultion results were not cached. A sure way to make sure you're starting with a clean slate is to (after you deploy your function and assemblies) go to Kudu and kill the non-scm w3wp process to see if that helps. I'd be curious to know if that does the trick as there are a few things we can to to improve this if it does.

Solution 2

Json.Net can be simply reference adding this line at the top of your Run.csx file :

#r "Newtonsoft.Json"

See this article if you want to know which assemblies are automatically added by the Azure Functions hosting environment:

Otherwise, if you want to use a specific version of Json.Net, you should probably add a reference to Json.Net using nuget package:

So you need to add a Project.json file that look like this:

{
  "frameworks": {
    "net46":{
      "dependencies": {
        "Newtonsoft.Json": "8.0.3"
      }
    }
   }
}

If your external dependency references Newtonsoft.Json without using a nuget package, you can have a look at this post that explains how to upload your binaries:

Solution 3

After some trial-and-error approach. I found what the issue was here.

@FabioCavalcante gave me a hint using a file-based reference,

#r "Newtonsoft.Json.dll"

It didn't work actually. I've copied those four files to Azure Functions' bin directory:

  • AzureFunctionsSample.Services.dll
  • AzureFunctionsSample.Services.pdb
  • Newtonsoft.Json.dll
  • Newtonsoft.Json.xml

It still gave me the same error, even though I did the file-based reference. Then, I found another file, AzureFunctionsSample.Services.dll.config that actually defines assembly binding redirects like:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

After I copied this config file to the Azure Functions' bin directory, it worked!


Lessons Learnt

  • Use the file-based reference for Newtonsoft.Json, #r "Newtonsoft.Json.dll", if your external assembly also has a reference to it.
  • Make sure that the assembly binding redirect configuration exists in Azure Functions' bin directory.

Correct me, if I'm still wrong.

Cheers,

Share:
18,358
justinyoo
Author by

justinyoo

Cloud 🥑 @ Microsoft English: https://devkimchi.com Korean: https://blog.aliencube.org Personal: https://justinchronicles.net GitHub: https://github.com/aliencube

Updated on June 11, 2022

Comments

  • justinyoo
    justinyoo almost 2 years

    I'm running an Azure Functions, called SmsWebhook. It calls a method in an external assembly, AzureFunctionsSample.Services.dll that has a reference to Newtonsoft.Json 8.0.3

    The details of my Run.csx looks like:

    #r "AzureFunctionsSample.Services.dll"
    using System.Net;
    using AzureFunctionsSample.Services
    
    public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
    {
        ...
    }
    

    Within the Run() method above, I create an instance and call a method in the instance. However, whenever I call that method, I receive the following error:

    2016-05-19T13:41:45  Welcome, you are now connected to log-streaming service.
    2016-05-19T13:41:46.878 Function started (Id=64fccf0c-d0ef-45ef-ac1c-7736adc94566)
    2016-05-19T13:41:46.878 C# HTTP trigger function processed a request. RequestUri=https://ase-dev-fn-demo.azurewebsites.net/api/smswebhook
    2016-05-19T13:41:46.878 Function completed (Failure, Id=64fccf0c-d0ef-45ef-ac1c-7736adc94566)
    2016-05-19T13:41:46.894 Exception while executing function: Functions.SmsWebhook. Microsoft.Azure.WebJobs.Script: One or more errors occurred. AzureFunctionsSample.Services: Could not load file or assembly 'Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040).
    

    I manually added the same version of Newtonsoft.Json.dll under the bin directory, but still got the same error. Why is it complaining at the Newtonsoft.Json.dll file?

    If I move all the logics within the external assembly into the Run.csx, it won't complain, by the way.

  • justinyoo
    justinyoo almost 8 years
    Unfortunately, this is not the case.I did add #r "Newtonsoft.Json" and using Newtonsoft.Json; in my run.csx file, even though these are not directly used in the file. I also added project.json with the NuGet reference like you suggested, but this didn't help me.
  • justinyoo
    justinyoo almost 8 years
    My issue is that the Json.NET library couldn't be loaded, even though it's there.
  • Thomas
    Thomas almost 8 years
    do you have the exact exception that is thrown ?
  • Fabio Cavalcante
    Fabio Cavalcante almost 8 years
    That is why I was asking about a file based reference (with the DLL extension) as that would trigger a private assembly load, as opposed to the shared assembly that doesn't match the version you expect. Can you give that a try?
  • Fabio Cavalcante
    Fabio Cavalcante almost 8 years
    Also, if you continue to see the issue after making that chang, please try to restart your site under settings.
  • justinyoo
    justinyoo almost 8 years
    @FabioCavalcante I did #r "Newtonsoft.Json.dll" and still doesn't work. However, it seems that I found what the issue here I have.
  • Fabio Cavalcante
    Fabio Cavalcante almost 8 years
    Do you mind sharing? I just finished testing your original scenario (dependency with a reference to Json.net 8.0.3) and with both deployed to bin, it works as expected. The Functions runtime will attempt to resolve private assembly dependencies from the private location. Is it possible you were missing the right assembly version when you first tried?
  • justinyoo
    justinyoo almost 8 years
    @FabioCavalcante I wrote another answer, instead of leaving here as a comment. Please have a look and correct me, if I missed anything.
  • Fabio Cavalcante
    Fabio Cavalcante almost 8 years
    I'm glad this is working for you! As mentioned in my previous comment, those steps shouldn't be required, though. Just having your direct dependency #r reference in the function and making sure the expected version of Json.NET is deployed into your bin folder should work as expected. Not sure if you want to go through those steps, but it would be interesting to have the setup described above, making sure you restart the host (to ensure a clean slate with assembly resolution) to see if that works. As mentioned, I reproduced your scenario and it worked as described here.
  • Fabio Cavalcante
    Fabio Cavalcante almost 8 years
    Another small clarification; you do not need to add explicit references to indirect dependencies. So the #r "Newtonsoft.Json.dll" (or with simple name) shouldn't be required at all.
  • justinyoo
    justinyoo almost 8 years
    Thanks! After I restart the function app itself (because basically it's a web app), I don't have to have a file reference, #r "Newtonsoft.Json.dll", but #r "Newtonsoft.Json" was enough, if I need to use it in the run.csx file. If I don't need it, the reference was not necessary.
  • justinyoo
    justinyoo almost 8 years
    But, if I don't put the Newtonsoft.Json.dll assembly into the bin directory, it gets an error. I'm still curious, why I should have the assembly, even though it's already internally referenced.
  • Fabio Cavalcante
    Fabio Cavalcante almost 8 years
    You shouldn't need the file reference if you're referencing the shared assembly (simple name), if you need a specific version, though, you'd place the file in your bin folder and reference with the extension.
  • Cameron Taggart
    Cameron Taggart about 7 years
    @FabioCavalcante Do you know how to avoid Could not load file or assembly 'log4net, Version=1.2.15.0? Any place I can add the binding redirects?