Cannot Load Assemblies For .Net Standard library (System.Text.Json)
Asked Answered
M

3

17

I am writing a .Net Standard 2.0 library that will be used by a binary PowerShell module. The library will be basically an API client with a lot of classes for dealing with the JSON responses. Prior to trying to deserialise the strings, I confirmed that the API was providing the JSON encoded string without issue.

As it was compatible with .Net Standard 2.0 when using the NuGet package, I thought I would try switching to System.Text.Json, rather than using NewtonSoft. However, it does not seem to have the version of particular assemblies it requires on certain platforms.

My Environment:

Windows 10
PowerShell 5.1 Desktop
.Net Framework 4.8
PowerShell Core 6.2.2
dotnet version 3.0.100

On PowerShell Desktop, I get the following problems when it has to deserialise anything:

PS dir:\> Import-Module '.\file.dll'
PS dir:\> [namespace.class]::TestMethod($string, $anotherString)  # Test method to return string
{"attribute":"value"}
PS dir:\> [namespace.class]::Method($string, $anotherString)  # Same as above, but uses System.Text.Json to deserialise
Could not load file or assembly 'System.Buffers, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The system
cannot find the file specified.

# System.Buffers.dll is with the System.Text.Json package, but seems to be the wrong version
PS dir:\> (Get-Item .\System.Buffers.dll).VersionInfo.FileVersion
4.6.26515.06
PS dir:\> [System.Reflection.Assembly]::LoadFile("$pwd\System.Buffers.dll").GetName().Version

Major  Minor  Build  Revision
-----  -----  -----  --------
4      0      3      0

On PowerShell Core there the same exception for another assembly/file.

PS dir:\> Import-Module '.\file.dll'
PS dir:\> [namespace.class]::Method($string, $anotherString)
"Could not load file or assembly 'System.Text.Encodings.Web, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. Could not find or load a specific file. (Exception from HRESULT: 0x80131621)"

#System.Text.Encodings.Web.dll is with the System.Text.Json package and appears to be the required version...
PS dir:\> (Get-Item .\System.Text.Encodings.Web.dll).VersionInfo.FileVersion
4.700.19.56404
[System.Reflection.Assembly]::LoadFile("$pwd\System.Text.Encodings.Web.dll").GetName().Version

Major  Minor  Build  Revision
-----  -----  -----  --------
4      0      5      0

Anyone got any advice on how I can resolve this without switching to Newtonsoft's JSON package? Falling back from System.Text.Json 4.7.1 to 4.7.0 or 4.6.0 introduces problems with other assemblies that are part of the NuGet package for System.Text.Json. I have read the advice here, but I either can't apply it here, or I simply do not understand.

Thanks in advance. Please let me know if you require more information, I will provide it.

EDIT

I updated the csproj as suggested by Gokhan

<PropertyGroup>
  <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
  <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>

This generated the following code in the appName.dll.config

<runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>

So the auto-generation of all the binding redirects is not working, as mentioned above there are at least two more that do not work. I would try manually creating them, but as far as I am aware, there is no source config file to put them in now. If anyone has any direction on that, I would appreciate it.

Melodee answered 27/2, 2020 at 15:42 Comment(0)
W
15

The problem you are hitting is because your library is targeting .NET Standard, which is not a runnable framework, so it sometimes has problems when trying to load it using models like the one Powershell does. Let me try to explain a bit more what is going on.

.NET Standard is simply an API surface area Spec, so basically just a set of APIs that will be guaranteed to be present and to be able to run on any runnable framework that implements that version of .NET Standard. What this means, is that if you have a library that targets .NET Standard, there is no real way to publish that library with all of its dependencies in a way guaranteed to run on any runnable framework, because each runnable framework might require additional dependencies for your library to load correctly. When referencing a .NET Standard library from a console application (either via Project reference or via a NuGet package) the console application will know which runnable framework its targeting, so it will be able to grab the right set of dependencies that your library will need at runtime, but the problem with your scenario is that this console app doesn't really exist, since you are loading it from powershell (which in a sense is basically the console app). Because of all of this, in order to have your library load successfully at runtime you will have to perform the work that a console app referencing your library would do, and pick the right references to carry along your library depending on the runtime that will be loading it. For powershell, there are basically two possible runtimes (.NET Core for powershell core, and .NET Framework for Powershell).

The easiest way to solve your problem, is to just create one dummy console app: from command prompt simply run dotnet new console -n dummyConsoleApp, set the targetframework to netcoreapp2.0 (assuming you are running on powershell core, if you are instead running on full powershell then set it to net46). Then add a project reference to your library like <ProjectReference Include="...<FullPathtoYourProject>\File.csproj" /> and then run from the command prompt dotnet publish -r win-x64 which should create a publish directory inside your bin folder which will have all of the assemblies that your application will use at runtime. After that, try loading your File.dll again, but this time from that publish folder, and you should be successful this time, since that publish folder will have all the right dependencies you will need for the runtime that powershell is running on. If for whatever reason this doesn't work for you, please feel free to log an issue about this in https://github.com/dotnet/runtime repo and tag me (@joperezr) and I'll gladly help you diagnose and fix the issue.

Washy answered 10/3, 2020 at 2:48 Comment(6)
Hi Jose. I really appreciate the great, detailed answer on this. It certainly helps a little. I guess I am a little confused about the .NET Standard stuff still. If I fell back to target net472 or lower, can I still use the PowerShellStandard.Library package so that the library will run on PowerShell Desktop and Core? Of if it needs to run on both runtimes, does it need dlls publishing for both runtimes? I have another module that targets 4.7.2 with the PowerShell 5 library and that runs fine cross platform, but don't know if I've just been "lucky". Apologies if I am still not getting thisMelodee
I was trying to follow the advice here with regards to the module. It looks like they are targeting .NET Standard 2.0 too.Melodee
Oh interesting I had actually never used the PowerShellStandard.Library before, it is very interesting and they even have their own template. So to answer your question, if you create a project that targets net472 or netcoreapp2.0 (or both) you will still be able to use PowerShellStandard.Library because that package targets .NETStandard 2.0 which is supported in both places. If you are using their template to create your module, simply set the property <TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks> and then create the two publish directories by running:Washy
dotnet publish -f netcoreapp2.0 and dotnet publish -f net461. This will create two separate publish directories, one for each framework. Then simply load your module from the net461 folder when running from Full Powershell, and from the netcoreapp2.0 folder when running from PowerShell Core. That should have both scenarios work, while only keeping one codebase.Washy
Jose, thanks for getting back to me. This is really helpful.Melodee
Happy to help, if you have any follow up issues or problems feel free to ping me. (I don't always check my stackoverflow inbox :P but if you ping me on an issue in the runtime repo in Github I'll get that right away)Washy
C
2

If you can't find the cause of version conflict for System.Buffers DLL, I think you can use assemblybinding in your config file to use version 4.0.3.0 instead of 4.0.2.0

<runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51"/>
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
      </dependentAssembly>
    </assemblyBinding>
 </runtime>

If you have other version conflicts then delete all dependentassembly tags in the config file(including the one above) and add this to the project (.csproj) file:

<PropertyGroup>
  <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
  <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>

When you build your project, you'll see (WebAppName).dll.config file in the output bin folder. Copy all assemblybindings from there to your source config file. Then delete the above code part from the project file.

Corin answered 27/2, 2020 at 16:24 Comment(1)
Hi Gokhan, thanks for your answer. In my dll.config file it has generated a binding redirect for System.Runtime.CompilerServices.Unsafe, but no other. Which source config are you referring to? As far as I was aware, .Net Standard projects do not have a config file, not one that I have seen anyway.Melodee
D
0

Open your solution in Visual studio.Check your reference for all dlls. Do you have duplicate dll one from Bin and other from nugget package? If you have Visual Studio 2017 version 15.7 or later, you can easily disable autogenerated binding redirects in the project's property pages.

Right-click the project in Solution Explorer and select Properties.

On the Application page, uncheck the Auto-generate binding redirects option.

Press Ctrl+S to save the change. Try building it. Check that you are referencing all Dlls from nugget location and nothing is from bin folder. If it build you are good to go otherwise do binding redirect compile again.

Disconnect answered 3/3, 2020 at 22:34 Comment(1)
The problem has never been the build. It has always built successfully, but complained about the dependencies in use. There's nothing here that I haven't already tried, unfortunately.Melodee

© 2022 - 2024 — McMap. All rights reserved.