Release multiple assemblies in single .net core package - c#

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)?

Related

TargetFrameworks in .NET Core

We are upgrading our project to .NET 6 from .NET Framework 4.5. We have a windows form application. Many of windows form commands has been depriciated in .NET 6. So to tackle this, I changed one of the Windows Forms project in the solution from:
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
</PropertyGroup>
to
<PropertyGroup>
<TargetFrameworks>net45;net6.0-windows</TargetFrameworks>
</PropertyGroup>
Now after changing this, I get the below error:
Project <another_project> targets 'net6.0-windows'. It cannot be referenced by a project that targets '.NETFramework,Version=v4.7.2'.
I understand why another_project is throwing this error message which is because the another_project is referenced in this Windows Form project and another_project's TargetFramework is .NET 6. So, I can change the TargetFramework for another_project to include .NET 4.5 as well.
But my question is, if I change this TargetFramework to TargetFrameworks and add multiple Framework there, does that mean my project is not upgrading to .NET 6 completely. Since it's using .net45 to complie/build in those cases, where it's failing to build in .NET 6. How does TargetFrameworks work??
I found the below link also, TargetFramework vs. TargetFrameworks (plural). But was not much helpful to understand this.
I'm assuming that you've upgraded your project files to SDK-style.
<TargetFrameworks>net45;net6.0-windows</TargetFrameworks> will effectively build your project twice - once for .NET 6.0 and once for .NET Framework 4.5.
(I'm not sure why your .NET Framework project is still targeting .NET 4.5 - I would have thought you should have retargeted it to 4.8 a long time ago...)
The output folders for these two targets will be:
For the DEBUG build:
\bin\Debug\net6.0-windows
\bin\Debug\net48
For the RELEASE build:
\bin\Release\net6.0-windows
\bin\Release\net48
Now if you want to reference one of those assemblies in another project, you're going to need to decide which to use, depending on the target framework for the dependent project.
In your case, it seems that you should be referencing the one in the "net6.0-windows" folder, so you should change your other project to reference that.
However, if you want the dependent project to ALSO be multi-targeted, you will need to change the hint path in the dependent project to use a compile variable to select the correct one.
For example, suppose your multi-targeted dependent project currently references a DLL using the following hint path:
<Reference Include="YourLibraryName">
path to referenced dll\bin\debug\net45\YourLibrary.dll
</Reference>
(Where "path to referenced dll" is whatever path is needed to locate the library.)
Clearly that will only reference the DEBUG Net 4.5 version of the library. You want it to reference the correct debug or release version and the correct .Net 4.5 or .Net 6.0 version. To do that, you can change the hint path to:
<path to referenced dll>\bin\$(Configuration)\$(TargetFramework)\YourLibrary.dll
At build time, $(Configuration) is replaced with the correct DEBUG or RELEASE string, depending on whether you're building DEBUG or RELEASE.
Similarly, the $(TargetFramework) will be replaced with the current build target, taken from the <TargetFrameworks>net45;net6.0-windows</TargetFrameworks> setting - either "net45" or "net6.0-windows".
Doing this will cause the correct DLL to be chosen from your multi-targeted project on which this project depends.
Note that this only applies to references using HintPath as above. If you're using project references, things would be different.
Also note that it's also possible (and maybe better) to create a NuGet package which will handle the dependencies etc properly, but that's a whole different story.
TargetFrameworks is used to build same project to multiple frameworks (so you have binary for each of them in output). It's usually used by libraries so they can be consumed by app targeted for different frameworks or, much less frequently, by apps, so they could be run on different machines.
I guess neither of theses is your cases. You just need to rework your app so it uses only features available in .NET Core.
Since you have added both net45 and net6.0-windows to your project it means that it should be compliable for both frameworks (i.e. it will be compiled twice, for each of the target frameworks), but you are trying to add reference to net6.0 project, which can't be used from net45. You need to either upgrade both to net6.0 only, or add net45 to the another_project.
Though usually it is another way around (compared to what you are trying to do), you use multitargeting for library projects (i.e. another_project in this case if I understand correctly) and keep one version for the executables (i.e. WinForms project should keep it's version).

Azure Function can't find a file/dll that it suppose to use

I have an azure webjob that used to work well. When I added an internal nuget of the company I work for and used it, I received the following error:
Could not load file or assembly 'System.Threading.Tasks.Dataflow, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.
The weird thing is that both the interal nuget, and a nuget that was defualtly existed in that project before, are using the System.Threading.Tasks.Dataflow, Version=5.0.0.0
When .NET was first created, the Windows-only .NET Framework, it has a concept called "strong naming" which makes the assembly version part of the assembly identity (not just the name, but also includes public key). With the .NET Framework when A.dll has a reference to B.dll, at runtime the version of B.dll must match the version A.dll was compiled against. The way to work around that was to use a binding redirect in an app.config or web.config file.
With .NET Core, they stopped enforcing this, but they still put in a check to make sure that the assembly found on disk is a HIGHER version than what the compile time reference was.
This is relevant because you're using Microsoft.NET.Sdk.Functions version 3.0.13, which targets .NET 3.1. Therefore, I assume you're Azure Functions app is running on .NET Core 3.1. I don't know what the assembly version of System.Threading.Tasks.Dataflow that ships in the .NET 3.1 runtime is, but I'd expect it to be version 3.something.
While NuGet package versions do not have to match assembly versions, it is very often the case that the major (and sometimes minor) versions do match, especially for System.* packages/assemblies.
Putting 2+2 together, you're running on .NET Core 3.1, which has System.Threading.Tasks.Dataflow.dll version 3.something, but your something.Kafka.dll assembly is compiled against System.Threading.Tasks.Dataflow.dll version 5.something. Since the compile time reference is a higher version of the version available at runtime, the .NET assembly loader refuses to use it.
There are two actionable things now.
Firstly, you can do one of three things to fix your project. Use a newer version of Azure Functions that targets .NET 5 or higher. Alternatively, use an older version of your whatever.Kafka package that doesn't depend on BCL (base class library) assemblies that are higher version than the .NET Core 3.1 runtime provides. Finally, I'm guessing the reason the dll doesn't exist in your project's bin directory is because of this issue: https://github.com/Azure/azure-functions-host/issues/5894
Secondly, contact the package owners of this Kafka package, and inform them that they don't need to use the Dataflow NuGet package. .NET Core 1.0 had it built into the BCL, so the package is only needed for .NET Standard and .NET Framework projects. If this Kafka package does support .NET Framework or .NET Standard, then if they change their PackageReference to include Condition=" '$(TargetFrameworkIdentifier) != '.NETCoreApp' ", then the package will be used only in .NET Framework and .NET Standard, and the BCL version of the package will be used for .NET Core and .NET 5 and above. If they use any other System.* package (that ships in the runtime), they should do the same. Of course, if the package owner refuses to remove Dataflow as a package dependency, or if there are other packages that bring in Dataflow as a transitive package, then you're stuck. But by informing the Kafka package owners of this, they at least have the opportunity to improve their package.

NuGet; Transitive Dependencies; Binding Redirect Hell

.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.

Where did all the assemblies go ? .NET 5.0 error CS1069

So my code which worked fine yesterday, screwed up when I added a .net 5.0 class library project to my solution.
Error CS1069 The type name 'Bitmap' could not be found in the namespace 'System.Drawing'. This type has been forwarded to assembly 'System.Drawing.Common, Version=0.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' Consider adding a reference to that assembly.
There is just one problem, when i look at what replaced "References" it now says "Dependencies" and when I try to add a dependency, "Add reference" is gone and there are no more .net assemblies being shown except under COM projects and type libs and when I add system.drawing etc, the error still remains and the code remains unreadable.
There are multiple reasons for what you're experiencing, I'll try to explain with a variety of assorted (and unordered) bullet-points:
Why isn't System.Drawing in .NET Core?
".NET 5" is the next iteration of .NET Core 3.1, not the .NET Framework 4.8 (.NET 5 comes immediately after .NET Core 3.1, there was never was a ".NET Core 4" to avoid confusion with .NET Framework 4).
.NET Core (including .NET 5) is designed to be cross-platform (i.e. to support Windows, Linux, macOS) with a single runtime.
Whereas previously people had to target .NET Framework for Windows, and target Mono, Xamarin, Unity, UWP, Silverlight, etc - which made multi-platform development in C# a pain.
Note that while Windows, Linux, and macOS now all share the real McCoy .NET 5 (and Silverlight is dead), other platforms like Xamarin, Unity, Mono, and UWP still have their own separate implementations of .NET (CLR+BCL) hence the need for ".NET Standard". At least we don't need those weird "Shared Projects" and "Portable Framework" projects anymore, phew!
In the .NET Framework, the System.Drawing API is just a .NET wrapper over Win32's GDI/GDI+, which means it's not cross-platform.
While System.Drawing seems like a platform-independent API, if you look closely at public types and methods like Graphics, Brush, Bitmap, Image and so on you'll see that they're all just thin wrappers and leaky-abstractions over GDI+. Mono does have System.Drawing reimplemented for Linux, however they did it by reimplementing GDIPLUS.dll which is about as horrible as it sounds.
So because System.Drawing is not cross-platform it was removed from .NET Core's "in-box" API.
So now you're wondering how you can get System.Drawing in .NET Core...
How can I get System.Drawing in .NET Core?
Earlier questions asked on StackOverflow from when .NET Core was more anaemic (and not yet pitched as a replacement for .NET Framework 4) have suggested switching to completely different and incompatible (but cross-platform-by-design) library, such as ImageSharp or ImageProcessor, however a better solution for Windows-only applications exists: the official Microsoft Windows Compatibility Pack (note that the aforementioned blog article is from 2017; as of 2021 the Windows Compatibility Pack is pretty-much fully implemented now).
All you need to do is open the NuGet package manager built-in to Visual Studio and add Microsoft.Windows.Compatibility as a package-reference and magically System.Drawing will be available for use in your application. You can also access the NuGet package manager via the Dependencies context-menu in Solution Explorer.
If you're using the .NET CLI ("command-line interface", not the "common language infrastructure", hurrah for overloaded acronyms) then just run dotnet add package Microsoft.Windows.Compatibility.
But why can't I add assembly references in .NET Core like I used to in .NET Framework?
You can!. It's just that (as of April 2021, running Visual Studio 2019 16.9) the UI for adding an assembly reference is kinda horrible.
You can do it manually by editing your .csproj and adding a <Reference Include="pathToDll.dll" /> (in the same <ItemGroup> as the other references).
You can do it from within Visual Studio by ignoring the missing menu option and using the Add Project Reference dialog:
Follow these steps
Go Solution Explorer > Your Project > Dependencies > Add Project Reference.
In the popup dialog, choose the Browse tab:
Then click the Browse... button:
Then browse for your target assembly DLL:
Voila - the added assembly reference will appear under a new Assemblies node under Dependencies:
Do note that (generally speaking, there are exceptions) you can only reference assembly DLLs that target .NET Core or .NET Standard. Because most DLLs built for .NET Core and .NET Standard exist as NuGet packages anyway there isn't much need to add an assembly reference directly.
Microsoft seems to have migrated Add Reference to standard SDK assemblies to "Manage NuGet Packages".. a step I do not understand the point of since the correct assembly is mixed in with third party user contributed search results.
Simply click where references would have been and is now dependencies, and right-click and then click Manage Nuget Packages, do a search for the assembly and VERIFY that Microsoft published it and it meets the .net Core requirements.
Yeah they dropped the ball on this in my humble opinion. Took me a minute to figure out that standard assemblies do not appear to be installed on the system.
The package gets installed under your user account's home directory, not in a system-wide folder requiring elevated user permissions to access the dll, so if your account is bugged, you get to a nice infected copy of the assembly each and every time :)

Nuget package manager installs dependencies for another target

So I have a DLL which is targeting multiple versions (.net 4.5, 4.6.1, netcore 2.0) which is pushed to a Klonkdike
Now I want to use this DLL, my project is .net 4.6.1, so I expect to resolve dependencies on this target only.
However my packages.config gets all .netcore dependencies. How can I prevent that?
This is the DLL:
And this is what is added when fetch this package:
I would expect to have only UAParser to be added and no other change since I already have the dependencies.
There are 3 folders in the lib folders of the package, so I would expect to only need these specific dependencies...
How can I avoid add all these dependencies?
UAParser is a pure .NET Standard based library.
.NET Standard versions lower than 2.0 depend on these libraries. However, the new tooling in VS 2017 (make sure you have at least 15.5.0) trims out these packages during build and makes sure the right assemblies are in place (these may also be System.* dll files but are not coming from these packages).
These dlls put in place by tooling are needed to implement the .NET Standard contract on .NET Framework - they forward to .NET Framework implementations.
In the upcoming .NET 4.7.2, the plan is to no longer need any of these DLLs. Until then, they are necessary.

Categories