Reducing dependencies for a .NET Standard class library? - nuget

I have converted some of my class libraries to .NET Standard with Visual Studio 2017.
This was easy, add a .NET Standard class library project in place of the original project and add all the files in there. The .csproj file even looks like a nuspec file now with package information and such. Inside the project options there was a checkbox for "Generate NuGet package on build", which I checked. Easy peasy.
However, .NET Framework consumers of my class library now gets a ton of dependencies, I counted at least 20 other nuget packages that were added, most of which was completely unecessary for my library. In other words, was "easy peasy too easy?"
Is this just a byproduct of me using .NET Standard as the only build output and I should add back a .NET Framework library as well?
Packages such as the following will be added to a project that consumes my library, even though they are completely unnecessary:
System.Security.Cryptography.*
System.Xml.*
System.IO.*
etc. there's plenty of packages being added. My library does "glorified" array analysis and doesn't require much at all.
The Visual Studio project is configured to target .NET Standard 1.0 and the only reference visible is the "NETStandardLibrary" so it's not like I added all of those myself.
I've inspected the package and it doesn't seem to list all of those either.
Can I add only the packages I need and still target .NET Standard 1.0?
My class library is open source here: https://github.com/lassevk/DiffLib
The nuget package is here: http://www.nuget.org/packages/difflib/2017.4.24.2347

This is quite a complex situation at the moment:
Can I add only the packages I need and still target .NET Standard 1.0?
Yes you can do it, but this is no longer recommended. In essence, .NET Standard is a specification that is made up of the packages referencing it. The supported way is to reference NETStandard.Library since it guarantees to bring you all needed compilation references and logic that you need in order to build correctly.
Beginning with the upcoming netstandard2.0, NETStandard.Library will be a flat package without dependencies and the individual packages will be removed from the dependency tree if your project or any other project references them. Also, NETStandard.Library will not be published as a dependency - so if you build a netstandard2.0 library, the resulting NuGet package will have no dependencies. (NETStandard.Library.NETFramework is required to be installed when using it in .net framework projects - NuGet is supposed to do this automatically).
That being said, if you really want to do it, you can set
<DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
in the csproj file and then add items like <PackageReference Include="System.[Something]" Version="4.3.0" /> for everything you need.

Related

How to wrap a NUnit Engine Extension as a NuGet package?

I am working on a nunit engine extension which will be shipped as a nuget package.
Following the advises in How to implement NUnit's NUnit.Engine.ITestEventListener i was able to write the extension.
This solution is working as long as the project which contains the extension (the .cs file as well as the .addins file) is being imported to the target project which will perform the nunit tests.
As soon as I create a nuget package (following Quickstart: Create and publish a NuGet package using Visual Studio (.NET Standard, Windows only)) from the extension project and install this package to a test project, the extension doesn't work anymore.
I assume there is a problem with providing the .addins file within the nuget package so that the nunit engine in the target project can find the extension.
I already tried to ship the .addins file within the nuget package by adding the following lines to the .csproj file of the extension project.
<ItemGroup>
<Content Include="file.addins">
<Pack>true</Pack>
</Content>
</ItemGroup>
If I add the .addins file to the target project by hand, the engine extension starts working.
How can a nunit engine extension be shipped as a nuget package without any adjustments by hand?
Im using NUnit(3.13.2).
Im quite new to nunit, nuget and writing questions on stack overflow. So if I'm missing something obvious here, I'm sorry.
This is one of those areas where I wish things were less complicated, unfortunately. Since extensions are found through a relative path from the NUnit engine to the package content, it depends on where both the engine and runner are located and where the package is located on your machine.
Here are some initial guidelines...
How to structure the package itself... your extension assembly itself should be in the tools directory. If there are other assemblies with it, which it references, it's best to also include a .addins file in the same directory, which lists that assembly on a single line. That way, the NUnit engine will save time by only examining the extension assembly.
A NuGet extension package will automatically be found by the engine if the runner has been installed as a nuget package as well. This works no matter how the packages are installed on your machine, i.e. using packages.config or in a nuget cache, provided both packages were installed the same way. (That proviso is a real gotcha and it may be that a future version of the engine needs to actually understand nuget.) See the addins file provided with the the NUnit 3 console runner as an example of why this works.
The same thing is true if both the runner and the extension are installed as chocolatey packages, because they are both in the chocolatey cache. If you do provide one (which I recomend) it has to be a "native" package - one that includes the actual binaries. A chocolatey package that merely invokes the nuget package will not work. See the source for any of the NUnit-provided extensions for an example of how this this is done.
If the executing copy of the engine (usually in the same directory as the runner) is anywhere else, there is no automatic way for the extension to be found. This includes the case where you are building a runner yourself and want the extension to be available while you are developing. In that case, you need to fully understand how the engine finds extensions (see the docs) and manually create an addins file (next to any that was distributed with the engine) containing the proper relative path.
This is especially complicated if you are developing an extension for general release. Then you have to deal with various runners installed in different ways by different people. OTOH, if you are doing this for internal use in your company, you may only need to deal with one of them. If you add more specifics about your goal to the question, I'll edit this with some more specific suggestions.

Can I create a NuGet package (or other package) with legacy dll's that are not on a .NET Framework?

We have a bunch of legacy dll's (basically could be anything, some are old fortran, some .NET) and we want to move them to Azure Artifacts. Can I create NuGet packages from these legacy dll's that are not based on .NET (like the Fortran ones) by themselves.
I've already tried creating the NuGet packages, but I get warnings on my dependencies because it looks like they are trying to load the packages on a .NET framework. Is the only real workaround here to build a .NET say class library or something, then reference the dll through that, and create a NuGet package with that library and just add the legacy dll's as references?
Can I create a NuGet package (or other package) with legacy dll's that are not on a .NET Framework?
The answer is yes.
You could target those legacy dlls to the tools folder instead of the lib folder. like:
<files>
<file src="legacy\*.dll" target="Tools" />
<!-- Other files -->
</files>
Then pack this .nuspec file when you build the pipeline, those legacy dlls are located in the tools folder, which will not add as references.
Check the From a convention-based working directory for some details.
Hope this helps.
I assume what you mean is that your managed code is usng DllImport attributes to call native code, which in the .NET ecosystem is called Platform Invoke, or P/Invoke. I mention this because if you googled terms around P/Invoke and nuget you probably would have had better luck finding existint stack overflow questions, blog posts and so on. For this reason it's useful to try to find out the official or commonly used names of the features you use, so you know what to search for. Unfortunately I don't think the NuGet team has any docs on this scenario at the moment.
SDK style projects support the runtime\ directory in the package, although I think that's somewhat undocumented as well. I don't know about traditional projects using PackageReference (PR), but packages.config (PC) definately does not support runtimes\. For PC projects, the package author typically (always?) includes build targets to copy the native assemblies after build. In the package, the native dlls are elsewhere, often the author puts them in the build directory next to the targets, but I think I've also seen the targets copy from the runtime directory so that the package supports both PC and SDK style projects.
I suggest you try to think of some commonly used native libraries that have .NET bindings and see how the package works (nupkg is just a zip renamed). My guesses would be sqlite or curl, or how asp.net core's kestrel web server bundles libuv (or did in earlier versions if it no longer does). I'm on vacation at the moment, so don't have the motivation to dig deep myself.

NuGet: references to assemblies in runtimes folder not added

I have a project that targets two different operating systems/frameworks:
net461 on Windows and
netcoreapp2.0 on OSX
I'm trying to figure out how to correctly package this for NuGet. According to this post I should be able to package them like this:
/runtimes/win/lib/net461/myassembly.dll
/runtimes/osx/lib/netcoreapp2.0/myassembly.dll
By when I add the NuGet package to another project, the packaged assemblies aren't added as references to the target project.
Then I read somewhere that you also need to add reference libraries to the /ref folder so I tried this:
/runtimes/win/lib/net461/myassembly.dll
/runtimes/osx/lib/netcoreapp2.0/myassembly.dll
/ref/net461/myassembly.dll
/ref/netcoreapp2.0/myassembly.dll
In this case the assemblies get added as a reference to the target project and I can build it, but the required assemblies aren't copied to the output folder.
The documentation on all this is extremely vague and I'm fairly lost.
What am I missing?
Associated NuGet Issue: https://github.com/NuGet/Home/issues/7316
Update: I've put together a sample project that demonstrates what I'm trying to achieve. In particular see the bottom of the readme, titled "NuGet Packaging".
This is what I've finally figured out/guessed (because as best I can tell there's no official documentation for some of this)
Files added to the /runtimes folder aren't automatically added as references to the target project.
The /ref and /runtime folder should be used in conjunction with each other and only for the .NET Core target. As best I can .NET Framework targets apparently don't support these folders.
The /ref folder is for compile time references and anything added here will be added as a reference to the target project.
Assemblies in the /ref folder don't need to have an implementation - every public API could just throw a not implemented exception. In practice however you typically just take a copy of one of the implementation assemblies and declare it as the compile time API.
I've read (but haven't tested myself) that assemblies in the /ref folder must be "Any CPU" builds. You can use CorFlags utility to patch an implementation assembly for this if necessary.
The /runtimes folder is used to provide an implementation assemblies for any references included in the /ref folder. These assemblies are used at runtime and during deployment.
The /runtimes folder can include additional assemblies that are only required at runtime and don't need to be seen by the client project. These additional assemblies won't be included as references in the target project but will be available for run/deployment.
As mentioned by others, the files in the /runtimes folder aren't copied to the output folder of the build. Instead config files are placed there that tell the runtime how to locate the /runtimes files from the NuGet cache.
For .NET Framework targets (ie: net461) just use the /lib folder as there's no other runtimes for .NET aside from Windows anyway.
Putting this all together, my original example, should have looked like this:
/lib/net461/myassembly.dll (net461/Windows Compile and Runtime)
/runtimes/osx/lib/netcoreapp2.0/myassembly.dll (netcore/OSX Runtime)
/runtimes/win/lib/netcoreapp2.0/myassembly.dll (netcore/Win Runtime)
/ref/netcoreapp2.0/myassembly.dll (netcore/* Compile Time)
I spent a fair amount of time trying your project on OSX in both Visual Studio for Mac and VS Code. I'll try to stick with factual observations without getting into "why don't you do X instead".
The runtimes/{rid}/lib/{tfm}/*.dll paths look ok
target="lib/{tfm}/..." assemblies are automatically referenced, runtimes/... are not
Using target framework of netstandard seems like it would make your package work in both netcoreapp and netstandard projects (e.g. use target="lib/netstandard1.6/..."). Compare with this
runtimes/ seems to be intended for platform-dependent assemblies you'll load at runtime. For example, 32/64-bit native assemblies in runtimes/win-x64/native/ and runtimes/win-x86/native/) loaded with AssemblyLoadContext (another post by McMaster)
Using separate slns for Windows and OSX, or separate platform-specific projects that reference platform-agnostic projects (like Xamarin) would obviate some of the configuration wrangling
I found no documentation on target="ref/...", but you can add Explicit Assembly <references> (inside the nuspec <metadata> block)
Packaged assemblies won't appear in the output directory, but when prepared for distribution with dotnet publish they'll be included:
.NET Core and .NETSTANDARD don't copy dependencies to output directory, they are mapped using deps.json which points to relative paths from local NuGet cache.
This has been a very useful thread to get more information and hints on how to create a NuGet package that references native DLLs, and is consumed in both .NET Framework as well as .NET Core / modern .NET libraries / applications.
My experience so far has been that if this library (let's call it library A) only targets .NET Standard, consuming this library in a .NET Core / 5.0 or 6.0 application does lead to the native assemblies being pulled in correctly from the runtimes folder. In a .NET Framework 4.7 application however, this does not appear to be the case. Unless the runtime is explicitly specified when compiling, e.g.:
dotnet build ... --runtime win-x86
When using library A in a .NET Core or .NET 5.0/6.0 application however, this runtime identifier is not required - all runtimes are made available and the right one is selected at runtime.
If you want library A to be consumed in applications that also target .NET Framework, and you don't want the user to have to specify the runtime explicitly, then it seems to be necessary to:
Target both .NET Standard and .NET Framework
Ensure that the native assemblies end up in the following folder structure in the NuGet package:
"lib/{tfm}/..."
While the .NET documentation referenced by tm1 earlier here talks about how to get this to work using nuspec files, it is less clear how to do so in the SDK .csproj format. I managed to do this in the NLoptNet project, see the relevant .csproj file here. Final relevant point (in addition to the two bullets above):
Use "<None Include" rather than "<None Update" to add the native assemblies
So far - this works, but there is one quirk - as you can see here it generates warning MSB3246 when consuming library A in a .NET Framework application. See also this Stack Overflow post. This leads me to believe that maybe the above is not the right approach, and therefore to some questions:
Is this the intended way to consume library A in .NET Core, modern .NET and .NET Framework applications?
Should one always specify the runtime identifier when using dotnet build / dotnet test?
Can you try to target .NET Standard 2.0 instead of net461 and netcoreapp2.0? Libraries built against netstandard2.0 should work with .NET Core 2.0 and .NET Framework 4.6.1: https://learn.microsoft.com/en-us/dotnet/standard/net-standard
Are you using the new csproj format? If so it has built in support for multiple target frameworks.
For example running dotnet pack against a .csproj file with this content:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net461;netcoreapp2.1;netstandard2.0</TargetFrameworks>
</PropertyGroup>
</Project>
will result in a .nupkg that works for .NET Framework 4.6.1, .NET Core 2.1 and .NET Standard 2.0.
Various trick can then be used to include specific parts for each framework depending on what's available.
I'm trying to solve the same problem.
The solution proposed by you works fine, but there is one question ...
The case of Win and net46 is clear. And now I need to add a reference to the assembly in the project for a netcoreapp for the Win and for Linux. The problem is that this is a DIFFERENT assembly with the SAME name. Those my package looks like this:
/lib/net461/myassembly1.dll (net461/Windows Compile and Runtime)
/runtimes/ubuntu/lib/netcoreapp2.0/myassembly2.dll (netcore/Ubuntu Runtime)
/runtimes/win/lib/netcoreapp2.0/myassembly1.dll (netcore/Win Runtime)
/ref/netcoreapp2.0/???
Update: Actually, the myassembly1.dll and myassembly2.dll are both called myassembly.dll. But to show that one is assembled for Windows, and the second one for Linux, I will leave here such a name.
The most interesting thing is that I tried to put any assembly in the folder ref, and it works on both Windows and Linux.
This version works on both systems
/lib/net461/myassembly1.dll
/runtimes/ubuntu/lib/netcoreapp2.0/myassembly2.dll
/runtimes/win/lib/netcoreapp2.0/myassembly1.dll
/ref/netcoreapp2.0/myassembly1.dll
And this too
/lib/net461/myassembly1.dll
/runtimes/ubuntu/lib/netcoreapp2.0/myassembly2.dll
/runtimes/win/lib/netcoreapp2.0/myassembly1.dll
/ref/netcoreapp2.0/myassembly2.dll
But I think this is not right and I was wrong somewhere.

Could not load file or assembly error in .Net Standard 2.0 class library

I have a .NET Standard 2.0 class library project with installed Nuget package System.Data.SqlClient version 4.4.0 and a Windows Form .NET Framework 4.7 project that has a reference to that class library.
Installing the Nuget Package and building the solution is successful. but in runtime every time that the code reaches a method that has any thing from SqlClient assembly inside it (for example an instance from SqlConnection) it gets this error:
Could not load file or assembly 'System.Data.SqlClient,
Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or
one of its dependencies. The system cannot find the file specified.
Considering this question, I assume the problem was also there in the last major version of the Nuget package.
Edit
I downloaded the Nuget package and unzipped it and from \ref\netstandard2.0 folder copied the System.DataSqlClient.dll manually in \bin\Debug folder of my Windows Form Project and now it works. The exact situation also happened with Microsoft.Win32.Registry package. So I was almost convinced that it's my fault and I'm doing something the wrong way, but when I tested it with System.Drawing.Primitive Package it worked perfectly without any need to copy a dll. Now I'm really confused.
I guess you may have figured it out already but hope it would help someone - wasted some time too on this.
So, in order to make everything work you would need to reference System.Data.SqlClient in Windows Form project that is referencing your .NET Standard Library
From that point everything should be working like a charm
As you already mentioned System.Data.SqlClient.dll was not in output directory.
Sounds like .NET Standard Library haven't grabbed with itself dependent library binary. There is nothing like "Copy Local" option in .NET Standard references so I don't see any way to check or set this behavior too
I had same problem.
Solution for me was adding dependecy from nuget for latest System.Data.SqlClient at my .NET Standard Library project.
I had the same problem. The .NETStandard assembly was added as a reference to my WPF project. I needed to make changes in the .csproj of the WPF project.
The solution mentioned in https://github.com/dotnet/sdk/issues/901 fixes it.
Steps:
Edit your core .csproj file in notepad.
Add the below two lines in each that you find in it.
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
Clean and rebuild your solution.
I had a similar problem, bindingRedirect helped in my case:
<bindingRedirect oldVersion="0.0.0.0-4.4.0.0" newVersion="4.4.0.0"/>

Cannot add Nuget packages to ASP.NET vNext project

So I decided to take a look at this new Visual Studio 14 CTP. When I tried to add a package from Nuget library (Raven Database Server precisely) to my ASP.NET vNext Web Application, it felt like nothing really happened. Not a single piece of solution has changed, and the References remained the same.
I tried adding it manually using my "Project.json" file (as seen in this article), but IntelliSense didn't provide me with any fill. I actually tried it in a lot of different ways, with a whole lot of Nuget packages, with the same result.
Can anybody explain this situation?
ASP.NET vNext supports two targets: .NET 4.5 and CoreCLR. When targeting .NET 4.5 (net45) you can add any existing NuGet reference. When targeting CoreCLR (k10) you can only add NuGet references that support it; today, only some packages support that.
The package that you mention is only available for net45. To add a reference to it, add the package as a dependecy in the net45 section of project.json - just like autofac is added in this file
The entry should be: "RavenDB.Server":"2.5.2879"
PS: the manage NuGet references dialog is not functional for ASP.NET vNext projects in this version of VS