I have a Web project (W) which has a reference to a Class Library project (C):
W --> C
C references N1:
C --> N1
N1 is a custom NuGet Package that has two dependencies (both are also custom NuGet packages): N2 e N3, targeting version 2.1 or higher and 3.4 or higher, respectively.
N1 ---> N2 (>= 2.1)
`--> N3 (>= 3.4)
When I added the N1 package to C, N2 had a 2.2 version available, so NuGet got that one, instead of 2.1, as it was expected to do.
The problem starts here: when I build W, N2 is not copied to W's bin folder. I googled a bit, and found out that Visual Studio does not copy dependencies that are not "first class dependencies", that is, dependencies which are not used directly by C, to W's output folder. I can confirm that, because when I add the method below to any class in C, N2 gets copied.
private void ForceCopyOfN2()
{
var someObject = new N2.SomeClass();
}
Also, without the hack above, if I downgrade the version of N2 to the same version that is specified by the N1 package dependencies (i.e. from 2.2 to 2.1), N2 also gets copied.
So I am guessing this is an issue with NuGet. Has anyone experienced anything like this?
I was not able to repro this with VS 2012 targeting 4.5, here is what I tried
1. Create new web application project
2. Create new class library
3. Add reference from web application to the class library
4. in class library
PM> install-package windowsAzure.servicebus
Attempting to resolve dependency 'Microsoft.WindowsAzure.ConfigurationManager (≥ 1.7.0.0)'.
You are downloading Microsoft.WindowsAzure.ConfigurationManager from Microsoft, the license agreement to which is available at http://go.microsoft.com/fwlink/?LinkID=235167. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'Microsoft.WindowsAzure.ConfigurationManager 1.7.0.3'.
You are downloading WindowsAzure.ServiceBus from Microsoft, the license agreement to which is available at http://go.microsoft.com/fwlink/?LinkId=218949. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'WindowsAzure.ServiceBus 1.8.0.0'.
Successfully added 'Microsoft.WindowsAzure.ConfigurationManager 1.7.0.3' to ClassLibrary1.
Successfully added 'WindowsAzure.ServiceBus 1.8.0.0' to ClassLibrary1.
5. where service bus is dependent on configuration with >=1.7 and 1.8 version of configurationmanage is installed
6. build the classlibrary and then webapplication solution, refresh the solution explorer. For my solution I saw classlibrary, servicebus and configurationmanager are dropped in the bin folder. (not sure why but i saw the files dropped after I tried building classlibrary once again after adding the reference)
Is there something I am missing here?
Related
How can I <Import> a .targets file that is included in <PackageReference>d NuGet package in a non-SDK-style web application project that targets .NET 4.7.2?
Background
The web application project is not an SDK-style project. It references a class library project in the same solution. This class library project is an SDK-style project. It has a package reference to Microsoft.Data.SqlClient version 4.1.0, which depends on Microsoft.Data.SqlClient.SNI.runtime 4.0.0. The latter contains native binaries which are automatically copied to the bin directory of the project.
The native binaries are not copied to the bin directory of the web application project. This is probably because of this:
SNI is the native C++ library that SqlClient depends on for various network operations when running on Windows. In .NET Framework applications that are built with the MSBuild Project SDK, native DLLs aren't managed with restore commands. So a ".targets" file is included in the "Microsoft.Data.SqlClient.SNI" NuGet package that defines the necessary "Copy" operations.
The included ".targets" file is auto-referenced when a direct dependency is made to the "Microsoft.Data.SqlClient" library. In scenarios where a transitive (indirect) reference is made, this ".targets" file should be manually referenced to ensure "Copy" operations can execute when necessary.
Recommended Solution: Make sure the ".targets" file is referenced in the application's ".csproj" file to ensure "Copy" operations are executed.
Source
Not sure what "built with the MSBuild Project SDK" means exactly, but I think my scenario qualifies.
Now I'm trying to implement the recommended solution, but I cannot get it to work.
What I did
Step 1: add package reference to Microsoft.Data.SqlClient.SNI version 4.0.0:
<PackageReference Include="Microsoft.Data.SqlClient.SNI">
<GeneratePathProperty>true</GeneratePathProperty>
<Version>4.0.0</Version>
</PackageReference>
I added <GeneratePathProperty> because of step 2 below. According to the documentation this should make a property available that can be used to refer to files in the package:
Sometimes it is desirable to reference files in a package from an MSBuild target. In packages.config based projects, the packages are installed in a folder relative to the project file. However in PackageReference, the packages are consumed from the global-packages folder, which can vary from machine to machine.
To bridge that gap, NuGet introduced a property that points to the location from which the package will be consumed.
Step 2: import the targets file:
<Import Project="$(PkgMicrosoft_Data_SqlClient_SNI)\build\net46\Microsoft.Data.SqlClient.SNI.targets" Condition="Exists('$(PkgMicrosoft_Data_SqlClient_SNI)\build\net46\Microsoft.Data.SqlClient.SNI.targets')" />
You can see that the package contains this targets file using the NuGet package explorer (link). The PkgMicrosoft_Data_SqlClient_SNI property is named according to the documentation (linked above) and based on their example:
MSBuild properties and package identities do not have the same restrictions so the package identity needs to be changed to an MSBuild friendly name, prefixed by the word Pkg.
Step 3: verify the tool versions on our agent:
MSBuild 16.11.2.50704 (installed on the agent C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\bin)
NuGet 6.1.0.106 (installed via a pipeline task)
According to the documentation linked above, these fulfil the minimum requirements (MSBuild 16 and NuGet 5).
Step 4: run nuget restore on our build agent and then build the project using the Azure DevOps MSBuild#1 task with msbuildArguments: '/t:Build'.
Step 4: collect output and publish as an artifact.
Outcome:
The SNI files are not present in the output. When building locally with MSBuild 17.0.0.52104 (installed in 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\bin') I do see the SNI files.
This use case might be a bit off-label, but I'm hoping someone can help me...
For a number of years I've maintained a "library" of MSBuild commands -- in a file we'll call CommonTasks.targets, distributed through our internal NuGet server -- that handles various tasks needed by my company's projects. For the last few years I've utilized GitVersion v3 with it, both for generating the library's own NuGet package version numbers as well as utilizing GitVersion's GetVersion MSBuild target directly (e.g., when generating a corresponding git tag), to avoid needing to extract the GitVersion-generated version from the dependent project's assembly, post-build.
Previously (with GitVersion v3), all that was required was for my CommonTasks.targets file to import the GitVersionTask.targets file located in GitVersion's NuGet package at \build\dotnet\GitVersionTasks.targets. This targets file in turn executed the necessary UsingTask commands (particularly GitVersionTask.GetVersion) which then let me call <GetVersion ...> in my own .targets file after the import.
With v4, GitVersionTask has introduced a dependency on UtilPack.NuGet.MSBuild (which I gather helps with various NuGet commands). To start with, that meant that I needed to import its UtilPack.NuGet.MSBuild.props file, or GitVersionTask wouldn't load at all. However, having done that, I now get unexpected errors attempting to call GetVersion from within an MSBuild file. Seemingly, the GetVersion target now attempts to query all my defined NuGet repositories (nuget.org, local caches, our company server, etc.) for the packaged version of my library, and at the end I'm greeted with this error:
MSBUILD : NuGetTaskRunnerFactory error NR0001: [NuGet Error]: Unable to find package CommonTasks with version
(>= 1.37.0-gitversiontask4-0001) [C:\redacted\CommonTasks\Build.proj]
MSBUILD : NuGetTaskRunnerFactory error NR0001: - Found 108 version(s) in Source1 [ Nearest version: 1.36.3 ] [C:\redacted\CommonTasks\Build.proj]
MSBUILD : NuGetTaskRunnerFactory error NR0001: - Found 42 version(s) in Source2 [ Nearest version: 1.36.3 ] [C:\redacted\CommonTasks\Build.proj]
MSBUILD : NuGetTaskRunnerFactory error NR0001: - Found 0 version(s) in Microsoft Visual Studio Offline Packages [C:\redacted\CommonTasks\Build.proj]
MSBUILD : NuGetTaskRunnerFactory error NR0001: - Found 0 version(s) in nuget.org [C:\redacted\CommonTasks\Build.proj]
MSBUILD : Task factory error NMSBT003: Failed to find main package CommonTasks#. [C:\redacted\CommonTasks\Build.proj(47,9): error MSB4175: The task factory "Ut
ilPack.NuGet.MSBuild.NuGetTaskRunnerFactory" could not be loaded from the assembly "C:\redacted\CommonTasks\packages\UtilPack.NuGet.MSBuild.2.7.0\build\\net46\UtilPack.NuGet.MSBuild.dll". Object ref
erence not set to an instance of an object.
What I don't get is why it's attempting NuGet retrievals at all, and, even if that's the new default behavior, I don't know a way to disable that, or barring that, I don't know why it's not working. I assume the error must have something to do with this not being a normal .NET project (i.e., with no source code aside from a .targets file, no .csproj file, etc.). But my library is used by many normal .NET projects, which themselves use GitVersionTask to auto-version their assemblies.
For now I've pinned my library to GitVersionTask v3, but would very much like to figure out how to allow it to use v4 as I'd been using v3 previously. I could likely switch to using the GitVersion.CommandLine package instead, and parsing its output, but I ultimately do need these GitVersion-generated versions as MSBuild properties in order to use them as intended. And with dependent projects also using GitVersionTask (not the CLI), it's convenient to be able to use a single package for both.
Is there a way to fix what I'm doing with GitVersionTask v4, or some completely different (perhaps more elegant) way to achieve what I'm trying to do?
When looking up the location that the NuGet packages are stored we noticed that some of the dlls were stored at C:\Users\Username\.nuget\packages while others were stored at C:\Program Files\dotnet\sdk\NuGetFallbackFolder.
For example, the microsoft.aspnetcore.all package folder is present in both directories on my dev pc (C:\Users\Username\.nuget\packages contains 2.0.7 and 2.1.0, while C:\Program Files\dotnet\sdk\NuGetFallbackFolder contains 2.0.0, 2.0.3, 2.0.5, 2.0.6 and 2.0.8).
According to the book "C# 7 and .NET Core 2.0 High Performance" by Ovais Mehboob Ahmed Khan. The default path should be C:\Users\Username\.nuget\packages.
Why are the files separated into the different folders?
Note: We are referencing microsoft.aspnetcore.all version 2.0.8 in most of our projects. Most of the other packages we reference in our projects alternate between the two folders.
.NET Core introduced the concept of Nuget caching (a bit similar to GAC). All packages downloaded only once to a specific folder and reused for all projects. It is needed because of .NET Core 2.x-3.x now composed from hundreds of packages. So the framework now is very granular you use what you need. This created a now type of issue the Nuget Hell (used to we had DLL Hell). To make developers life easier MS introduced "aggregating" packages e.g. microsoft.aspnetcore.all (which contains nothing just references everything but actually if you do self contained build it will include only what you are using not all DLLs).
As you mentioned now you can find installed Nuget packages under you User account e.g.:
C:\Users\< your userName >.nuget\packages.
According to this documentation you can run this command which will list all Nuget folders on your machine:
dotnet nuget locals all -l
The result will be something like this (folder names self are explanatory):
C:\Users\...>dotnet nuget locals all -l
info : http-cache: C:\Users\...\AppData\Local\NuGet\v3-cache
info : global-packages: C:\Users\...\.nuget\packages\
info : temp: C:\Users\...\AppData\Local\Temp\NuGetScratch
info : plugins-cache: C:\Users\...\AppData\Local\NuGet\plugins-cache
I have a component which was developed for Apache Karaf 4.0.5. However, it turns out it needs to be deployed on glassfish 4, which is based on an older version of Felix. I get this message when trying to deploy it:
missing requirement [319.0] osgi.extender; (&(osgi.extender=osgi.component)(version>=1.3.0)(!(version>=2.0.0)))
However, my pom file does not show any explicit dependency on osgi.component. I think I must have used some OSGI R5 (or R6?) specific feature, but I don't know which would cause this error.
How can I either convince glassfish to accept my bundle, or change the bundle's build / pom so that I get warnings when trying to use OSGI features dependent on R5 or later?
You are probably using a recent version of bnd to generate your bundle and its metadata. Bnd will generate the requirement on the osgi.component extender (This is DS) when your bundle has DS components and either you require something new in DS 1.3 like field injection or you are using the very latest version of bnd.
You can disable the generation of the requirement in your bundle with the bnd instruction:
-dsannotations-options: norequirements
https://bnd.bndtools.org/instructions/dsannotations-options.html
At the company I work at we do the following when we need references to third party dlls in our projects:
Use nuget to get package
Pull dll's out and create a "lib" folder and add the references here
this lib folder is added to git so other team members have all references when they do a pull from git
Reference dll's stored in lib folder in our project
We do this to have full control and know exactly what references we are using.
My question is how is this achieved when using vnext and can we continue to do it this way?
Have watched "INTRODUCING: The Future of .NET on the Server" and it seems you list all dependencies in project.json file and when you do k restore it will go and download all based on feeds in nuget config file
You'll make use of the project.json file. As you mentioned, you list all your dependencies in there and the K Package Manager will deal with resolving the missing packages for you.
You'll notice that in the json file you specify the package in somewhat of a key-value pair of package:version. Most examples show a version of * which means get me the latest. But there's nothing stopping you from specifying a specific version, or a specific part of a version. For instance, the project.json file in the Autofac container of the DI project specifies a specific version of Autofac:
"dependencies": {
"Autofac": "3.3.0",
"Microsoft.Framework.DependencyInjection": ""
},
The main DI project specifies a sort-of-specific version of Microsoft.Framework.ConfigurationModel:
"dependencies": {
"Microsoft.Framework.ConfigurationModel": "1.0.0-*"
},
That says get me the most recent build of 1.0.0
This system allows you to automatically get the latest and greatest if you want, but also specify a specific version for safety. There's no reason to copy DLL's into a custom lib folder.
EDIT: You inspired me to blog about it: http://davidzych.com/2014/08/13/specifying-package-dependency-versions-in-asp-net-vnext/
Just noting that "-*" does not necessarily return the latest version. It my simple testing, it always returns the lowest available version. Per this documentation the calculation is more complex and returns the lowest version that "works".
EDIT: added link to documentation