.NET Core - build project specifying ReferencePath - c#

I have a .csproj for the .NetCore platform with classic references. I'm using the hintpath attribute for the development environment. But I should build csproj on the CI-environment where referenced assemblies are placed in the different directory.
On the classic net4 I've used the /p:ReferencePath argument for the MSBuild tool.
But the "dotnet build" has no similar argument.
As a fallback I found the "dotnet msbuild" command but this tool is ignores the /p:ReferencePath=xxx argument and shows me
warning MSB3245: Could not resolve this reference. Could not locate the assembly "AssemblyName". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors.
Please guide me, what can I check, where dotnet-build/dotnet-msbuild tools are searching the referenced assemblies and how to specify that directory?

Problem is coused by Microsoft.NET.Sdk.props: AssemblySearchPaths has no ReferencePath.
Fixed by adding to csproj:
<PropertyGroup>
<AssemblySearchPaths>
$(AssemblySearchPaths);
$(ReferencePath);
</AssemblySearchPaths>
</PropertyGroup>

You can still build .net CORE/Standard projects in solution using MSBUILD.
It is seem to be a bug which I reported to Microsoft that (and this is not about core/standard but rather new project file format) referencePath is ignored with new project file format.
Supply add /t:restore to msbuild command along with build target, so it will restore and build at same time.
The work-around for your CI/Build server situation is to create a special solution configuration, and add similar to following into your project file
<Choose>
<When Condition="'$(Configuration)|$(Platform)'=='YourSpecialConfiguration|x64'"><!-- attention here -->
<ItemGroup>
<Reference Include="your.dllname">
<HintPath>yourSpecialPath\your.dllname.dll</HintPath><!-- attention here -->
<Private>true</Private>
</Reference>
<!-- more references here -->
</When>
<Otherwise>
<ItemGroup>
<Reference Include="your.dllname">
<HintPath>yourRegularPath\your.dllname.dll</HintPath><!-- attention here -->
<Private>true</Private>
</Reference>
<!-- AND more references here -->
</Otherwise>
</Choose>
This will allow you to just change configuration name in CI/Build and will do the job.

But the "dotnet build" has no similar argument.
Why are you saying that?
The dotnet cli still support "property injection" with -p instead of /p. Link (Search for "-p")
For your question, the build command will look like this command:
dotnet build -p:ReferencePath=xxx

Related

DLL in nuget references Version=0.0.0.0

I have the following situation:
There is a library LibF which I build as a nuget package, it contains dlls for net48 and net core 3.1. I am the person packing it directly from the csproj using:
<PropertyGroup>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
It has the version="4.2.0-alpha.55" (I use GitVersion). Then I have another library LibD that uses LibF, so it references it as a nuget package in the .csproj like this:
<ItemGroup>
<PackageReference Include="LibF " Version="4.2.0-alpha.55" />
</ItemGroup>
Then I also create a nuget package from LibD. I am the person packing it directly from the csproj using:
<PropertyGroup>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
When I try to consume the nuget of LibD somewhere else I get a compile issue in Visual Studio 2019:
Error CS0012 The type 'MyType' is defined in an assembly that is not referenced.
You must add a reference to assembly 'LibF , Version=0.0.0.0, Culture=neutral,
PublicKeyToken=468d6536c503beba'.
Obviously I don't have version 0.0.0.0. Now when I look at the nuget package (unzip) I see the following entry in the LibD.nuspec:
<dependency id="LibF" version="4.2.0-alpha.55" exclude="Build,Analyzers" />
This is correct. But when I use JetBrains DotPeek to analyze the dll it tells me in the references of the dll:
LibF, Version=0.0.0.0, Culture=neutral, PublicKeyToken=468d6536c503beba
So my question is: Why is there Version=0.0.0.0 as a reference for the dll? Why does it not want 4.2.0.0 because that is the version I have and the version I would expect to be referenced by the dll.
What could lead to such an issue?
What was missing was the following in the PropertyGroup of the csproj:
<UpdateAssemblyInfo>true</UpdateAssemblyInfo>
Afterwards version of assembly is correct.

dotnet pack for .NET Core project produces .nupkg file with sources

I have a simple .NET Core 2.1 class library project and need to pack it as a Nuget package. For that I'm using this command:
dotnet pack <project>.csproj --output <outputFolder> /p:Configuration=Release
But, I'm getting this warning:
C:\Program Files\dotnet\sdk\2.1.502\Sdks\NuGet.Build.Tasks.Pack\build\NuGet.Build.Tasks.Pack.targets(202,5): warning NU5100: The assembly 'obj\Release\netcoreapp2.1\<assembly>.dll' is not inside the 'lib' folder and hence it won't be added as a reference when the package is installed into a project. Move it into the 'lib' folder if it needs to be referenced.
I understand that lib folders are for targeting multiple frameworks, but I don't want that. I only want to target .NET Core 2.1.
At the end my .nupkg file is created, but it has all the source code (don't know why) and when used in other projects, they cannot reference the assemblies, because they are inside the bin\Release\netcoreapp2.1 folder.
I've seen some guides to create Nuget packages from .Net Core projects and none of them mention something related to lib folders.
I need to understand if something is missing or if I'm doing something wrong.
Thanks.
Edit: Added the project file and the .nuspec file
Project File:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<Company>xxx</Company>
<RootNamespace>xxxx</RootNamespace>
<Product>xxxx</Product>
<Authors>xxxx</Authors>
<NuspecFile>xxxxx.nuspec</NuspecFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="5.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.3" />
</ItemGroup>
</Project>
.nuspec File:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>xxxxxx</id>
<version>1.0.0</version>
<title>xxxx</title>
<owners>xxxx</owners>
<description>xxxx</description>
<authors>xxxx.</authors>
<copyright>Copyright 2019 xxxx.</copyright>
<summary>xxx is a common code library for internal projects.</summary>
</metadata>
</package>
There appear to be a few misunderstandings here.
Firstly, the lib folder in the nupkg is not for multitargetting, but for all dlls that are part of the build. If your package only supports 1 target framework moniker (TFM), then your nupkg will only have a single folder under lib, for example lib/netstandard2.0/MyLib.dll, or possibly lib/netcoreapp2.1/MyLib.dll if your app uses APIs that are part of the .NET Core 2.1 runtime, but not netstandard. If your project only uses APIs that are part of netstandard, there's no benefit to targetting netcoreapp and has potential problems which might cause you issues in the future, even if it works fine today.
Secondly, a simple class library (to me this means the project only contains .cs files and no content files, no build file, the package will only contain the dll and NuGet's own files) shouldn't need to know anything about what's inside a nupkg. Even more complex projects that multitarget don't need to care about the lib folder when using an SDK style project. Just specify your target TFMs in the <TargetFrameworks> element, and let the SDK pack the nupkg itself. It knows what to do. If you've done anything in your csproj to try to force the output dll to a different location within the nupkg, it's more likely to cause problem than improve things.
Without seeing your .csproj, I cant' guess what you could have done to get that warning message on pack, but like I said, a brand new dotnet new classlib packs just fine, and if your project only contains code files, you shouldn't need anything in the csproj related to pack paths.

Restoring dependencies in Visual Studio Code is using the wrong source/feed

I'm not sure how to set the default nuget feed for my .net core project in Visual Studio Code to https://api.nuget.org/v3/index.json
When I attempt to add a package (and subsequently, restore dependencies), I get the following errors...
C:\Program Files\dotnet\sdk\2.1.403\NuGet.targets(114,5): error : Unable to load the service index for source https://smartassessor.pkgs.visualstudio.com/_packaging/SANuget/nuget/v3/index.json. [c:\Users\Matthew.OConnor\Desktop\Important Documents\Programming\DatingApp\DatingApp.API\DatingApp.API.csproj]
C:\Program Files\dotnet\sdk\2.1.403\NuGet.targets(114,5): error : Response status code does not indicate success: 401 (Unauthorized). [c:\Users\Matthew.OConnor\Desktop\Important Documents\Programming\DatingApp\DatingApp.API\DatingApp.API.csproj]
This source https://smartassessor.pkgs.visualstudio.com/_packaging/SANuget/nuget/v3/index.json has nothing to do with my current project, however it is used for other projects that are typically run using full blown Visual Studio. Those projects are saved in a completely different place to this project.
I simply want to be able to add nuget packages from nuget.org in my .net core project. How do I do this in VS code?
I don't currently have a nuget.config file in this project.
The package source mentioned in the error appears to be coming from a package source I have setup whilst using Visual Studio
This is my csproj file...
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4"/>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="4.0.1"/>
<PackageReference Include="CloudinaryDotNet" Version="1.3.1"/>
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.1.0-preview1-final"/>
</ItemGroup>
</Project>
CloudinaryDotNet is the package that generated the errors above.
I think VS Code is just running a dotnet restore, and the reason you're seeing this source being used is because it's configured in your User/Computer nuget configuration file (located on windows, which you seem to be running, at %appdata%\NuGet\NuGet.Config & %ProgramFiles(x86)%\NuGet\Config respectively). The VS configuration editor you showed is just a nice GUI for this configuration file.
If you want to keep this general setting, you should be able to use a nuget.config file in your VS Code project (which you mentioned you don't have at the moment). There is more info on this here -Add custom package source to Visual Studio Code.
Also, if you're trying to restore manually, you can use one of these 2 flags -
dotnet restore --source https://api.nuget.org/v3/index.json
dotnet restore --ignore-failed-sources
These are pretty self explanatory, but you can see the full documentation here - https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-restore?tabs=netcore2x
Hope this helps (:

Referencing common binary in multiple projects

I have a third party DLL which I need to reference in multiple c# projects in a solution.
It is presently referenced as follows.
<Reference Include="Contoso.App, Version=4.0.5.0, Culture=neutral, PublicKeyToken=xxxxxxxx, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\Contoso\4.0.5.0\Contoso.App.dll</HintPath>
</Reference>
I have around 40 projects in my solution which reference the Contoso.App.Dll
Whenever the DLL version changes a new folder is created as follows
..\ThirdParty\Contoso\5.0\
I have to go and update all my 40 projects as follows.
<Reference Include="Contoso.App, Culture=neutral, PublicKeyToken=xxxxxxxx, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\Contoso\5.0\Contoso.App.dll</HintPath>
</Reference>
Is there a better way to manage the version change of the DLL?
Can I create single variable in the solution and reuse it across all the projects?
Private NuGet repository is prefect, but requires too many changes. A simpler way is to create a common project and let other projects reference this common project.
common.props. It's better to use solution relative path instead of ...
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Reference Include="Contoso.App, Culture=neutral, PublicKeyToken=xxxxxxxx, processorArchitecture=MSIL">
<HintPath>..\ThirdParty\Contoso\5.0\Contoso.App.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
Import it in other project.
<Import Project="<MySolutionPath>\common.props"/>
There may be build errors in VS after changes are made in common.props because the reference is updated instantly. Verify it via command line msbuild.exe first.
Setup a Private Nuget Repository that the rest of your team can access.
See Scott's answer.
tl;dr
Open Visual Studios
Create a new Empty Web Project
Add the Nuget.Server Package
Package manager console: install-package nuget.server
Overwrite web.config: Yes
Open Web.config
Set appSettings
packagesPath: where the nuget is going to sit
Init:
nuget init c:\source c:\localnuget
Push:
nuget push {package file} -s http://localhost:51217/nuget {apikey}
Alternate hosting
Just to add flexibility to TriV's comment, you can define an environment variable (e.g. CONTOSO_VERSION) and use it in the pre-build event command to copy the DLL into your bin folder (or wherever you're referencing from) using $CONTOSO_VERSION. This way you can change the referenced DLL version back and forth via env variable. Make sure it's pre-build event for a project others depend on (or create a dummy project with others depending on it for a solution-wide pre-build event).

How to create a nuget package with both release and debug dll's using nuget package explorer?

I'm using the Nuget Package Explorer to create some nuget packages. I've managed to do so just building a project in Release mode in VS and adding both the dll and pdb files to the package.
So far so good, but when I add the package to another project and try to step into the code while debugging, it will step over it instead.
I understand that I need to build and add the Debug dll and pdb to my package if I want to step into the code while debugging. I'm not sure though how to add these to the package I've already create, which already contains the Release dll and pdb file, which are named the same.
Any thoughts?
My thoughts are, NuGet packaging is a lot about conventions.
There is no problem in packaging same namespaces and same names for different platforms (as in lib/net40/mydll.dll, lib/net35/mydll.dll etc in the same package), as NuGet will filter registered dependencies by platform.
Building several versions for the same platform seems unconventional, this discussion is biased towards making a package per build. That doesn't mean you can't do it, but you should first ask yourself if you should.
That said, if your debug and release builds are very different (conditional compiling etc) this might useful though. But how will end-users choose Release or Debug when installing your package?
An idea could be, one version per build configuration. Both can be installed into the project. To do that, either add a targets file to your package or build a powershell install script (unsupported since Nuget v3) that adds conditional references directly in the target project file, if you want something less basic than whatever MsBuild can do for you.
Example of the first tactic: Create a .target file (in your package, create a build folder and then create build\YourLib.targets with the following contents):
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<Reference Include="YourLib">
<HintPath>..\packages\YourLib.1.0.0\lib\Debug\YourLib.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<Reference Include="YourLib">
<HintPath>..\packages\YourLib.1.0.0\lib\Release\YourLib.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
Providing you created debug and release folders (platform folder is optional), the build output will effectively change depending on configuration - provided packet consumers have conventional configuration names, but you could always extend the condition logic a bit with $(Configuration).Contains etc or just put that in the package readme
Inspired by #Tewr I've found a cumbersome but a working solution.
Create a nuget with the following file structure:
lib\net\$(Configuration)\YourLib.1.0.0.dll <---- put here some dummy file named YourLib.1.0.0.dll
tools\release\YourLib.1.0.0.dll <--- put here the release version
tools\debug\YourLib.1.0.0.dll <--- put here the debug version
build\YourLib.targets
The targets file content:
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CopyReferences" BeforeTargets="Build" Condition="Exists('..\packages\YourLib.1.0.0\lib\net\%24(Configuration)')">
<Exec Command="mkdir ..\packages\YourLib.1.0.0\lib\net\Release" />
<Exec Command="mkdir ..\packages\YourLib.1.0.0\lib\net\Debug" />
<Exec Command='copy "..\packages\YourLib.1.0.0\tools\Release\YourLib.1.0.0.dll" "..\packages\YourLib.1.0.0\lib\net\Release"' />
<Exec Command='copy "..\packages\YourLib.1.0.0\tools\Debug\YourLib.1.0.0.dll" "..\packages\YourLib.1.0.0\lib\net\Debug"' />
<Exec Command='rmdir /S /Q "..\packages\YourLib.1.0.0\lib\net\%24(Configuration)"' />
</Target>
The dlls in lib folder will be automatically added as references creating the following in the project file:
<Reference Include="YourLib>
<HintPath>..\packages\YourLib.1.0.0\lib\net\$(Configuration)\YourLib.1.0.0.dll</HintPath>
<Private>True</Private>
</Reference>
Once you build the project for the first time, the target will copy the release and debug version from tools\release and tools\debug folders to lib\net\release and lib\net\debug folders. In the end, it will delete the lib\net\$(Configuration) folder
Enjoy (or not - I personally don't like the solution).
Thanks #Tewr In new nuget format and sdk style csproj format, we can use some constant as $(MSBuildThisFileDirectory) to get the current file path.
The code that uses the version will increase maintenance difficulty. The sdk style csproj format use the new package format that will do not output the package file to package folder.
We can add the targets file to build folder and use $(MSBuildThisFileDirectory) to get the file path.
<ItemGroup Condition="'$(Configuration)' == 'DEBUG'">
<Reference Include="YourLib">
<HintPath>$(MSBuildThisFileDirectory)..\lib\debug\YourLib.dll</HintPath>
</Reference>
</ItemGroup>
See the file

Categories