.NETCore just litters your disk a lot worse, too many versions, too many assemblies, too many standards and no GAC. Hopefully they'll get their act together sometime soon. – Hans Passant Aug 17 '17 at 10:37
No, it just keeps getting worse. : \
Have a .NET Standard 2.0 class library that references Microsoft extension classes. When we deploy to the server, we get runtime binding exceptions. My questions first:
Why aren't binding redirects being generated for transitive dependencies?
Since they're not, how do I come up with a full list to add manually?
How does the compiler know what version to redirect to unless it intends for me to deploy the version it compiled against?
How do I come up with a list of DLLs to deploy - excluding framework DLLs but including anything that wouldn't be on the server?
Is a nuget package broken if the assembly version in \ref\ is lower than the assembly version in \lib\?
Details:
We have a class library compiling against .NET Standard 2.0... it references Microsoft.Extensions.Configuration.Json.
MimExtension
\--Dependendencies
\--Packages
\--Microsoft.Extensions.Configuration.Json (5.0.0)
\--System.Text.Json (5.0.0)
\--System.Buffers (4.5.1)
System.Buffers resolves to \.nuget\packages\system.buffers\4.5.1\ref\netstandard2.0\System.Buffers.dll. The file version in that directory is 4.6.28619.1, date 2020/02/19. .NET Reflector shows the assembly version as 4.0.2.0.
The \lib\ version of that DLL is \.nuget\packages\system.buffers\4.5.1\lib\netstandard2.0\System.Buffers.dll... same file version and date, but the assembly version is 4.0.3.0.
Compiling the DLL gives me a .dll.config file with binding redirects that I could copy into the consuming application's app.config - but System.Buffers.dll and System.Text.Json.dll aren't there. Microsoft.Extensions.Configuration.Json.dll also isn't there - though another nuget package, Microsoft.Extensions.Configuration.Abstractions.dll, is.
I'm assuming this means the compiler thinks no redirect is necessary for the DLLs that aren't in there (see question #3). It makes sense that only DLLs with conflicts across references get added to the binding redirects (if that's what's happening), but conflict or not, our app won't bind to the \lib\ version of the System.Buffers.dll the compiler uses and RTE's (question #1).
To resolve this I can add binding redirects manually. But how do I look at all the nuget references in my project and determine (recursively) what version was chosen for each dll? Short of dumping verbose build output to a text file with some fancy regex and an hour of copy and paste, that is (question #2).
Note: I can add <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> to the .csproj file and get all referenced dlls dumped to the output directory - including .NET dlls like System.Threading.dll and System.Runtime.CompilerServices.Unsafe.dll- but that still doesn't get me a full list of what versions each one are... especially since I need assembly versions, which I can't even display in explorer.
Regarding that... some of the binding redirects generated automatically are for .NET assemblies like System.Threading... does VS really expect me to deploy the version of System.Threading I compiled against? For that dll, I have newVersion="4.0.11.0"... our server has assembly version 4.0.0.0, file version 4.8.3761.0. VS expects me to deploy assembly version 4.0.11.0, file version 1.0.24212.01 (wtff?!?). The 4.0.11.0 version pulled down by nuget is dated 2019/12/26... the 4.0.0.0 server version is dated 2021/01/21.
I'm guessing that's a Core vs. Framework versioning wtf - but binding redirects don't care. The app that will load our library is .NET Framework 4.8... am I supposed to deploy the System.Threading 4.0.11.0 dll with my app, or manually change the binding redirect and let it load the server's version? It's absolutely ludicrous that a core DLL has a higher assembly version than its newer .NET Framework counterpart (question #addingnewonesasigo).
So when we're referencing nuget packages, how do we know what needs deployed and what doesn't (or worse, shouldn't be)? (question #4) I feel like the build process should copy dlls that aren't part of the framework/won't be in the GAC to the output directory - but there's nothing TIAO to indicate that in the nuget package specs.
Regarding #5... shouldn't the dlls in a nuget package have the same version in the \ref\ and \lib\ folders? The breakdown in Microsoft.Extensions.Configuration.Json is in System.Text.Json... S.T.J's .nuspec lists a .NET Standard 2.0 dependency to <dependency id="System.Buffers" version="4.5.1" />. So why would the System.Buffers.dll nuget cache have different versions in \ref\ and \lib\? Shouldn't they both be either 4.0.2.0 or 4.0.3.0?
There are a lot of questions out there on this - even some specifically to System.Buffers. But nobody has resolved this satisfactorily (that I can find) for a class library. I'm going to try adding a scratch website to the solution and reference the library - just to see whether .NET gets the necessary dlls/redirects in place for its only love: Web
Update
I manually added a binding redirect to 4.0.3.0 for System.Buffers... and immediately got the next mole to whack: Could not load file or assembly 'System.Numerics.Vectors, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies
And so it will go, until we find a way to list everything that VS probed. And without 100% regression coverage, there's no guarantee we won't miss something when we deploy.
For an executable, dotnet publish; and ship the resulting folder is always correct.
But for a dll compiled against .net standard; I've only had success building a nuget package and referencing it and letting the compiler (whole package thereof) figure out what final dlls the project needs. You can make a nuget package with dotnet pack.
I have never needed binding redirects to link .netstandard to .net framework.
Compiling the library for specific platforms pulls the dependent DLLs into the bin folder. This makes sense in retrospect - compiling for .NET Standard is only meaningful when the target platform isn't known and won't be chosen by the developer of the library. That scenario requires a centralized package manager.
Targeting for .NET 4.8 (highest version currently supported by MIM) gave us the DLLs in the bin directory and set binding redirects in the config file.
That said, the System.Buffers.DLL version issue only went away because the .NET 4.8 dependencies were defined correctly. The Microsoft.Extensions.Configuration.Json .nuspec indicates a different assembly version than the dll that gets downloaded when compiling for .NET Standard 2.0. I don't know if that's an issue with the references when the .NET developers compiled the nuget package or an unavoidable artifact of nuget packaging itself.
Related
I have a VS extension, targetting .NET 4.6.1. I've refactored some of it into a .NET standard 2.0 dll, referenced by the extension. Where the extension depended on System.Data.SqlClient, the .NET standard dll depends on Microsoft.Data.SqlClient, added as a nuget package. The trouble is, at runtime, I get...
Could not load file or assembly 'Microsoft.Data.SqlClient, Version=1.11.20045.2, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5' or one of its dependencies
Following this question, I thought I'd look in the VSIX and sure enough, Microsoft.Data.SqlClient.dll is gloriously missing. I'm not very good with reference issues, but it seems this is totally out of my hands?
I really need this to work, and will happily screen-share or otherwise help anyone who can fix my problem. If you prefer to see the problem in situ, you can clone https://github.com/bbsimonbb/query-first.git then checkout the branch target-core-and-framework.
Your problem might be caused by how NuGet dependencies are resolved. Your extension project "A" references a .NET Standard project "B" that in turn depends on a NuGet package "Microsoft.Data.SqlClient". Therefore, "A" has a transitive dependency on "Microsoft.Data.SqlClient" through "B". In other words, "A" needs "B", as well as the NuGet package to work.
As I can see from thelinked repository, your extension project uses the old-style format for .NET Framework projects, not the new SDK-style format like your .NET Standard library. When using the old project format, project "A" will only get assemblies of direct dependencies like "B" in its output folder, not indirect dependencies like the NuGet package. This is why the assemblies are not found. With the new SDK-style project and PackageReference, these transitive dependencies are respected. Hence, the package assemblies will be copied to your output folder or extension.
In order to make this work, you need to migrate your .NET Framework project to the new SDK-style format and use PackageReference for packages. It seems that this might be tricky for Visual Studio extensions, but it is possible and you can find more on it in this question. If this is not possible in your case, I guess you will have to resort to use plain old references to the assemblies that you require.
I have a UWP app and a bunch of NETStandard 2.0 libraries. The libraries contains a lot of logic and communication interfaces that I am using in ASP.NetCore and Desktop WPF applications, so I am not going to change them.
I use NuGet packages in the libraries so I could reference those in NetCore and NetFramework projects as well. I would like to reference these packages in the UWP project - cause this is the whole point of this standardization concept isn't it?
For the UWP project this does not work. I have the latest Nuget package of System.ServiceModel.Primitives which is 4.5.3 (DLL version 4.5.0.3 - don't ask why) and PublicKeyToken is b03f5f7f11d50a3a - this can be referenced in every project except UWP.
Regardless of every attempt:
- I have installed the Nuget package for the project
- I have referenced the version explicitly in the CSPROJ file
- I have removed every other reference to SDK file version
But still, the UWP project looks for the dll in
C:\Program Files (x86)\Microsoft SDKs\UWPNuGetPackages\microsoft.netcore.universalwindowsplatform\6.2.8\ref\uap10.0.15138\System.ServiceModel.Primitives.dll
That DLL has version of 4.2.1.1 and the PublicKeyToken is cc7b13ffcd2ddd51
Because the two tokens are different - binding redirects does not work - or maybe I am missing something. Does anyone have a workaround for this?
The whole solution is in VS2019 and I have the latest Win10 SDK installed 10.0.18362.1.
Thank you in advance.
EDIT: I forgot to mention, I have a repro on GitHub
You can create a separate .NET Standard project and incapsulate all your logic (and usage of System.ServiceModel.Primitives assembly) into this project. Then you can add it as a reference to the project with UWP executable.
Also you can setup a binding redirect for strongly-named assemblies by specifying codebase according to this article
I've used https://icanhasdot.net to analyze the NuGet dependencies of a sample project I've been experimenting with. Here are the results:
The graph doesn't show specific versions, but I'm targeting .NET Framework 4.5 for the project and based on the "Manage Nuget" view in Visual Studio I know that all the NuGet Packages (i.e. all the green squares in the graph) in my project require Newtonsoft.Json >= 6.0.8, e.g.:
I want to use a slightly newer version of Newtonsoft.Json for my project, so I've added version 8.0.2. Since this is definitely >= 6.0.8 I wouldn't expect this to cause problems. However, when I run the program I immediately get a System.IO exception saying that Newtonsoft.Json 6 something was not found.
I've fixed this problem by adding an app.config file with an assembly binding redirect (Assembly Binding redirect: How and Why?) to the newer version of Newtonsoft.Json and this fixed the problem. However, I don't understand why such a binding would be required if all the NuGet packages my project depends on require >= 6.0.8, which 8.0.2 definitely is.
The .NET runtime has a concept called strong naming.
I'm probably getting lots of technical details wrong, but basically an assembly that is not strongly named effectively says "my name is zivkan.utilities.dll", and when another assembly is compiled against my assembly, the reference says "I need the class named zivkan.utilities.thing from zivkan.utilities.dll". So, it knows nothing about versions and you can drop in any zivkan.utilities.dll that contains a zivkan.utlities.thing class and the runtime will try to run it.
If I strong name sign zivkan.utilities.dll, now the assembles advertises itself as "my name is zivkan.utilites.dll version 1.0.0 with public key ..." (I'm going to leave the public key part out for the rest of my answer). Now, when another assembly is compiled against it, the compiled reference says "I need zivkan.utilities.dll version 1.0.0". Now when this execute, the .NET runtime will only load zivkan.utilities.dll version 1.0.0 and fail if the version is different, like you saw, you get an error. The program can have a binding redirects to tell the .NET runtime assembly loader that when it sees a request for zivkan.utilties between the versions of 0.0.0.0 and 2.0.0.0 to use version 2.0.0.0, which is how you solved your problem.
NuGet versions and assembly versions are two separate concepts, but since they're typically the same value (or a very similar value), it's not so different. Assembly versions are a run-time thing, while NuGet package versions are a build-time thing.
So, imagine the situation without binding redirects. Your program, CommandLineKeyVaultClient is loaded and has a dependency on Newtonsoft.Json version 8.0.2. The .NET runtime loads Newtonsoft.Json.dll and confirms that it is indeed version 8.0.2. Then the .NET runtime sees that CommandLineKeyVaultClient also has a dependency on Microsoft.Rest.ClientRuntime.dll, let's say version 1.0.0.0. So, the .NET runtime loads that dll and confirms the assembly version number. Microsoft.Rest.ClientRuntime.dll has a dependency on Newtonsoft.Json.dll version 6.0.8. The .NET runtime sees that Newtonsoft.Json version 8.0.2 is already loaded, but the version doesn't match and there's no binding redirect, so let's try to load Newtonsoft.Json.dll on disk (there's actually a hook you can use to tell the loader to load the dll from a different directory, when you really need to load different versions of the same assembly, you can). When it tries, it sees the version of the assembly doesn't match the strong named dependency, and fails saying "can't load Newtonsoft.Json.dll version 6.0.8", which is true because the version on disk is actually 8.0.2.
If you use NuGet packages using PackageReference, NuGet will look not only at transitive NuGet dependencies, but also project dependencies and build a graph of all assemblies (project or nuget) that are needed. Then MSBuild should automatically detect when two different assemblies depend on different versions of the same assembly name and generate binding redirects. Therefore, when using PackageReference, this should not generally be a problem.
However, if you use packages.config to define your NuGet dependencies, NuGet will try to add binding redirects when it detects a version conflict (which I think you can opt-out of). But since this is calculated at the time you modifiy NuGet dependencies in that project(install, upgrade or uninstall a package), it's possible to get the binding redirects out of sync, and there's an issue with project to project dependencies and what NuGet packages those project references use.
Anyway, I hope this explains why you get the dll loading error when all your projects have NuGet dependency >= 6.0.8. Again I repeat that assembly versions and NuGet versions are different things, even when they have the same value, and the .NET runtime allows you to load different versions of the same assembly at the same time and needs instructions when you don't want that, which is what binding redirects are.
I think that there are some errors in the selected answer, and in the comment threads. I'll try to clear it up.
First, you must know that package version and assembly version are two completely different things. You could publish version 1.0.0 of a package, and the assemblies in the package could be version 99.0.0.0. That might be a bad practice, but nothing prevents it, and in fact some Microsoft-published packages do this, I'm sure for good reason.
The .Net Framework (meaning NOT (.Net Core, or, .Net v5 or v6)), enforces an exact version match when resolving a reference to a strongly named assembly at runtime, by default. That is, if you build an assembly A that had a build-time reference to v 1.0.0.0 of assembly B, then by default it will require version 1.0.0.0 of assembly B at runtime, assuming that B is strongly named. The only way to defeat that is to use BindingRedirect, or publisher policy, which itself requires use of the GAC.
It's understandable that you thought that the Nuget versioning rule (e.g., >= 1.0.0) would have some relation to the assembly binding mechanism, but it doesn't. It just specifies what newer package versions you will automatically accept when running "NuGet Update". It has no bearing on the runtime behavior of accepting newer versions of assemblies than the version that was used at build time.
.Net v5 and v6 (maybe also Core 1 and 2) applies no meaning to strong naming, and so does not enforce any version match. If you require different behavior, you can use the https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/understanding-assemblyloadcontext API.
Strong naming in .Net Framework is principally a tool that lets different versions of an assembly load simultaneously side-by-side in a application domain, at runtime. That capability requires use of the GAC. SN is essentially meaningless in .Net core.
I'm trying to release my library (lz4net) for .NET Core. The library "architecture" differs a little from "normal" as it contains multiple assemblies but only one should be referenced, the other ones are just its dependencies. They are not separate packages though.
So, assembly LZ4.dll should be referenced by the application, while LZ4pn.dll should not, it just should be there as LZ4.dll (main one) may use it. `LZ4pn.dll" is not usable on its own therefore it is not released as separate package.
It seems to be working fine in: net2, net4, and pcl, but it netcore does not "see" LZ4pn.dll if it is not referenced by application.
NOTE: I've checked and C:\Users\xxx\nuget\packages\lz4net\1.0.11.93\lib\netstandard1.6 (after installation of .nupkg) do contain both assemblies.
So, LZ4pn.net is not in <references> section as it should not be referenced by application directly. It is not in <dependencies> section as it is not external dependency which needs to be separately downloaded. It is just there - in netstandard1.6 subfolder of nuget package. .NET Framework (2+) work fine but .NET Core does not discover this assembly.
Any help?
Maybe you know some open-source package which uses the same approach (multi-assembly packages which reference only top assembly)?
I just switched from (an older) Microsoft.Bcl.Immutable NuGet package to System.Collections.Immutable and was surprised to find all these new package dependencies in my project:
System.Collections
System.Diagnostics.Debug
System.Globalization
System.Linq
System.Resources.ResourceManager
System.Runtime
System.Runtime.Extensions
System.Threading
They are listed as dependencies of the NuGet package, so they have a right to be there, yet they are obviously also already installed on my PC and my target environment (Azure btw) as they come with the framework.
I already have a large number of packages in my project and would like to avoid the additional overhead caused by these 8 packages, if possible (and without shooting myself in the foot).
Is it safe to remove these dependencies?
Do I now have to use these packages throughout my project because they might differ from their installed versions and some portion of my project might now use the wrong ones? (due to some DLL linking madness?)
Edit: Just for completeness, as there was a comment before: The dependencies are actual packages (not namespaces) and have to be downloaded, I'm targeting and compiling with .NET 4.6, working in VS2015. It's entirely possible though that something is outdated and the packages do not have to be loaded normally?
You are just seeing a side-effect of the Nuget package having to keep a lot of people happy. The package supports an enormous number of targets, it is proliferating rapidly as of late. I see support for Xamarin for OSX and iOS, Windows Phone 8.0 and 8.1, Windows Store, CoreCLR (the open source project), .NET 4.5, MonoTouch for iOS and Android and .NETCore (Silverlight).
These dependent packages just contain reference assemblies, the kind that are normally installed in your c:\program files x86\reference assemblies directory. The Nuget package doesn't take the chance that such a reference assembly might be missing and includes the whole kit and kaboodle.
After it is all downloaded, the package installer runs and adds the references you actually need in your project. Easy to see what happened, just open the References node of your project. If your targeted the desktop version of .NET 4.5 and up, the grand total of added references is one, just System.Collections.Immutable. Yes, you can remove them.