Where to store version for .NET core project for NuGet packaging, etc - nuget

I'm using TeamCity to build and publish my .NET core application to NuGet.
I've been using the project.json file for storing the version, but with the latest version of .NET core the project.json file has become obsolete, since everything has moved to the project's .csproj file. So I'm trying to find a new home for the version information.
Here are some possibilities that I can think of.
Continue using project.json, but only for storing the version information (doesn't feel like a permanent solution)
Storing the version in a separate file like version.txt
Somehow storing it in GitHub using tags or something alike. This feels like the ideal solution.
I like option 3. the most, e.g. for storing release points (1.0, 2.0, etc.) and then using some date time appended afterwards. But I'm unsure if that's possible or even recommended.
What is the recommended way of storing the version information, given that you have your project in GitHub and that you're using the latest .NET core?

Is there a problem with having it in the .csproj file?
As for recommendations - there are various ways you do this which you choose is up to you.
With .NET Core the assembly information can be specified in the project file (.csproj). How you have it defined in the project file is up to you.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.0</TargetFramework>
<AssemblyVersion>1.4</AssemblyVersion>
</PropertyGroup>
</Project>
You could have it hard coded. Or run some other MSBuild task to generate it.
AssemblyFileVersion and AssemblyInformationalVersion should work in the same way.
When you build the project an AssemblyInfo.cs file is generated in the obj/Debug/netcoreapp1.0/ directory.

Related

What is nuget props file and what is it for?

I have looked around and read a lot but couldn't find a definitive answer that would explain as to what the "nuget.props" file is and what is it used for.
Any explanation and maybe with some example?
Starting with some background information, .NET projects are built with MSBuild. A C# project's .csproj is just a MSBuild project file with a file extension that signals by convention that it's C# and not some other language, but to MSBuild it's just a project file. MSBuild has only a few base types, properties, items, targets and tasks. By convention, properties and items go in files with extension .props, while tasks and targets go in files ending in .targets. That's why if you look at old-style csproj files you'll see <Import Project="path\to\Microsoft.Common.CSharp.props" /> and <Import Project="path\to\Microsoft.Common.CSharp.targets" />. New, SDK style projects is basically syntactic sugar to do exactly the same thing.
Next, the MSBuild and .NET teams made the build system extensible. So, rather than being limited to what Microsoft built into the C# compiler/build system, you can replace parts of the build system, or add additional things into it. Without NuGet, the way to do this is to create your own .props and .targets file somewhere, then edit your .csproj and add <Import ... /> statements. This can work fine if your props and targets are in the same source code repository as what's using it, but editing your csproj and hardcoding the path to the props and targets files doesn't work so well otherwise.
NuGet can help with this. If you create a package with the appropriate conventions, NuGet will make sure the props and targets are discovered and used in the build. With projects using packages.config, NuGet will edit the csproj for you on install/upgrade/uninstall. Projects using PackageReference, NuGet will write a file to the intermediate directory (obj/ folder) named nuget.g.props and nuget.g.targets, which imports all the props and targets files from all the referenced NuGet packages, and the build system uses these files.
The first example I could think of why someone would want to do this is if you want to use a newer version of the .NET compiler than is installed on your system. Simply reference the Microsoft.Net.Compilers package, and the .props and .targets in the package will replace the compile targets/tasks in the system-installed build system, and use the one from the package instead. This allows you to use new language features before the compiler is installed on your system, or if you want to make sure all builds of your code use the same compiler, even if different developers or CI agents have different versions of things installed.
Another example may be pre-compiled scripts. If you have your own scripting language, create build tools that converts them into C# files, then write MSBuild props and targets that will run before the "real build" to convert your custom language into C#, save the generated .cs files into the intermediate folder, add MSBuild Compile items for these generated files, then the C# compiler will compile it with all the other .cs files in the project. You'll need a reasonable amount of knowledge of MSBuild and the .NET build system, but it's possible.

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"/>

declaring nuget packages that depend on each other in one solution

We have solution with a lot of projects and a more or less complex dependency graph between those projects. Now each of those projects should become its own nuget package and the dependency graph of the nuget packages should mirror the on of the projects.
I have two questions:
Is it possible to achieve this while keeping all projects within the same solution? If so how?
Is advisable to keep all projects in the same solution? What would be the a common / "best practice" approach to this?
The situation in our project is the same and we took the following approach:
The first step is to create the nuspec files defining your packages. We have placed all theses files in a folder named ".nuspec" which is located in the solution's root directory. The nuspec files are added to the solution in a solution folder that is named ".nuspec", too.
The solution itself has a global AssemblyInfo file that contains the versioning information as well as some copyright stuff - in short all information that is common between our projects. Each project then has its own assembly info adding the information specific to each project.
The nuspec files do not contain a version. Instead we use $(version) as a placeholder there:
<?xml version="1.0" encoding="utf-16"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MyCompany.MyProduct.Server.DataAccess</id>
<version>$(Version)</version>
<authors>MyCompany</authors>
<projectUrl>http://example.com/myProduct.html</projectUrl>
<iconUrl>http://example.com/myProduct.icon.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Some description goes here.</description>
<summary>The summary goes here</summary>
<copyright>Copyright © MyCompany 2015</copyright>
<language>en-US</language>
<dependencies>
<dependency id="MyCompany.MyProduct.Common" version="$(Version)" />
<dependency id="MyCompany.MyProduct.Server" version="$(Version)" />
</dependencies>
</metadata>
<files>
<file src="path\to\MyCompany.MyProduct.Server.DataAccess.dll" target="lib\net45\MyCompany.MyProduct.Server.DataAccess.dll" />
</files>
</package>
(Of course the dependencies might have dependencies themselves. The server component might reference a logging component for example.)
Initially we created a console application reading the version of the solution from the global AssemblyInfo file and parsing it into all of the nuspec files before creating and publishing the packages.
The console application worked well, but was a bit tedious to maintain in a TFS environment with continuous integration enabled. So we defined a custom TFS build template doing this work. All we need to do now to create a set of nuget packages for all of our projects is to trigger a TFS build.
This approach has the advantage that all packages have the same version and thus work well together.
This approach has the disadvantage that all packages have the same version and cannot be released independently.
We chose that approach because it prevented us from producing a conglomerate of badly integrated components. Our projects provide a small framework that is used to develop small LOB-applications that all are quite similar. Due to the fact that we deliver the framework in a set of different packages the developers can choose which of the packages they actually need and then install only those. Should a developer decide to add a missing functionality lateron he just installs the relevant packages that have the same version as those already installed. Thus there's no need to worry about compatibility.
Currently, in VS 2017 you can have several library projects in a solution which are built into separate packages and also reference each other by <ProjectReference>. Surprisingly, VS is smart enough to use <ProjectReference> when building solution AND to produce correct package <dependencies> for referenced projects in nuspec. In other words, you can conveniently work with many projects simultaneously in one solution and have them all published as a set of packages depending on each other.
Yes, you can "probably" make this work. I say probably because I haven't tried it, but I would approach it with something like this: https://www.nuget.org/packages/CreateNewNuGetPackageFromProjectAfterEachBuild/ with a manual nuspec defining your references would work. If you wanted to get really fancy, you could write some post-build Roslyn code to parse your project dependencies and build up the nuget dependency tree. That said, don't do this, in a non-trivial solution, its almost guaranteed to become manual and brittle pretty fast.
Ultimately, it's much preferable to just break your solution up -- create a solution per Nuget package, and pull in your dependencies using Nuget itself. Assuming you have a build/CI server, this should be fairly trivial. Just run your own Nuget repo and publish the build artifacts as they get built -- that way your dependent projects will pull the latest package you JUST built. You'll want to ensure your build process refreshes Nuget each time and you can use the standard nuget spec command as a post-build step. As a nice bonus, it'll force everyone working on the code to really think thru the dependencies when making changes.

NuGet pack is not packaging all files in the output directory. What am I missing?

I'm trying to create a NuGet package from a .csproj. I have successfully compiled the project and the output folder contains all of the necessary files (my assembly and all of its dependencies). However, NuGet only seems to be placing the assembly created by the .csproj into the package and not any of its dependencies. My command line looks like this:
nuget pack MyProject.csproj -Property Configuration=Release
and my resulting .nupkg file only has my assembly in the lib folder. I have successfully gotten NuGet to work for other projects, but it just so happens that this project is referencing the Enterprise Library logging block, but it was NOT retrieved via NuGet. I'm not sure if that could be related to my problem or not.
Any thoughts on why it's not picking up the dependenices?
If you need to keep your nuspec file up-to-date automatically, its really just an XML file (as I'm sure you know) so there are some very nice tools you can use from MSBuild to automate nuspec creation/updates. Out of the box, MSBuild provides a few tasks that can update or transform XML, and I've used MSBuild Community Tasks to customize the initial nuspec. For example, the default nuspec contains a few lines with broilerplate that I don't need, so I use XmlUpdate tasks to delete them.
Although I have not looked into scanning the csproj file for non-nuget references, I think its likely possible with a little research. Here are some links to blog entries describing my experiences with NuGet automation, they may help you get a head start:
Creating Packages with NuGet the MSBuild Way - This article includes some basic NuSpec updates because the package described is not that different from the type of package NuGet already knows how to automate.
Manage Your MEF Parts With Nuget - This article includes some more complex updates to support distributing MEF parts as runtime-only references.
If you plan on doing this alot, don't want to mess with MSBuild, or just want to get back the behavior you liked from the pre-1360 version of ProjectFactory.cs, NuGet supports third-party extension through MEF. You could go into the source control and grab the earlier code that you liked and create a custom command (for example custompack) that provides that behavior. Then you could use it from the command line like so:
nuget custompack MyProject.csproj -Property Configuration=Release
I think this is a really cool aspect of NuGet but I haven't played with it myself yet. Here is an article that explains how to do it:
Extend NuGet Command Line
So even though David mentioned that NuGet is not designed to support this scenario, if the scenario is correct for you then you can go this route to extend NuGet to meet your needs.