DLL in nuget references Version=0.0.0.0 - c#

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.

Related

Roslyn analyzer not loading in dotnet build

tl/dr;
My project-referenced Roslyn analyzer works fine in Visual Studio 2019 but fails to load in dotnet build with the following build warning:
CSC : warning CS8032: An instance of analyzer Nearmap.CodeAnalysers.UseCtorInjection.UseCtorInjectionAnalyser cannot be created from [solution path]\MyCodeAnalysers\bin\Debug\netstandard2.0\MyCodeAnalysers.dll : Could not load file or assembly 'Microsoft.CodeAnalysis, Version=4.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified.
Details
I have a Roslyn analyzer in a project which is loaded into the other projects in the solution.
The analyzer works as expected, and produces the expected warnings, during build in Visual Studio 2019.
When I try to build the solution from the command line (as our CI pipeline does) using dotnet build MySolution.sln I get the following build warning for every project that references the analyzer project:
CSC : warning CS8032: An instance of analyzer Nearmap.CodeAnalysers.UseCtorInjection.UseCtorInjectionAnalyser cannot be created from [solution path]\MyCodeAnalysers\bin\Debug\netstandard2.0\MyCodeAnalysers.dll : Could not load file or assembly 'Microsoft.CodeAnalysis, Version=4.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified.
where [solution path] is the full path to the folder containing the solution.
The analyzer project looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<!-- added in an attempt to fix the problem -->
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
</ItemGroup>
</Project>
Projects reference this analyzer as:
<ItemGroup>
<ProjectReference Include="..\MyCodeAnalysers\MyCodeAnalysers.csproj"
PrivateAssets="all"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
</ItemGroup>
I've checked the build output folder of the analyzer project and it contains all the DLL dependencies needed to load the analyzer, but it's obviously not being looked at when try to load the analyzer.
What am I missing?
The problem was the SDK version.
Aparently the Microsoft.CodeAnalysis.dll (and others) are loaded from the .Net SDK in use. We're currently still using the .Net 6.0 SDK to build our solutions. .Net 6.0 includes Microsoft.CodeAnalysis.dll 4.1.0 not the 4.4.0 version I was using. VS2019 runs the build differently, and didn't have the DLL resolution problem.
I fixed it by rolling the package references back to v4.1.0:
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.1.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" />
</ItemGroup>

.NET Core - why do 'classic' assembly references not get put into the generated <app>.deps.json files at a higher level?

In .NET core, the generated .deps.json file controls assembly loading - if your dependencies aren't in the .deps.json for your top level application, they will not get loaded unless you start handling AssemblyResolve events and all that stuff.
The situation I have is as follows
.NET Core 6
Class Library Assembly - lets call it 'ClassLib'
Application (exe) - lets call it 'App' - that depends on 'ClassLib' as a project reference
If I use a Nuget package (PackageReference) inside ClassLib then the Nuget package shows up in the generated App.deps.json and everything works. (Newtonsoft.json used as an example of this below)
However, I have several cases where there are legacy assemblies that I wish to reference that are not in Nuget packages. Those can be added as references using the UI (Add COM Reference then 'Browse' to the assembly) or via a <Reference ...> node in the csproj.
When you build 'App', the App.deps.json does not include any sign of the dependencies on the legacy assemblies via ClassLib, just the nuget packages. This means that at runtime, the legacy assembly is not going to get loaded, leading to all sorts of interesting failures...
Details of the situation
ClassLib.csproj contents
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
</ItemGroup>
<ItemGroup>
<Reference Include="Legacy">
<HintPath>..\path\to\Legacy.dll</HintPath>
<SpecificVersion>True</SpecificVersion>
</Reference>
</ItemGroup>
</Project>
App.csproj contains
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ClassLib\ClassLib.csproj" />
</ItemGroup>
</Project>
Generated App.deps.json shows the dependency of ClassLib on NewtonSoft.Json (imported as Nuget) but not on Legacy.dll
"ClassLib/1.0.0": {
"dependencies": {
"Newtonsoft.Json": "13.0.2"
},
"runtime": {
"ClassLib.dll": {}
}
}
I have tried various combinations of options in the node such as CopyLocal/Private etc with no change to the outcome in terms of the generated App.deps.json
I can make things work if I pack Legacy.dll into a nuget package, but to be honest I have a number of legacy dlls to deal with and making each into a nuget package (they come from various sources and may be updated separately) seems rather a 'sledgehammer to crack a nut' solution.
so...
Is there a way that I can persuade the build system to treat the old-fashioned assembly reference in the same way as the package reference and propagate the dependencies up to higher level projects? Failing that, is there a way that you can customize the build process to inject dependencies into the .deps.json file at build time? (hey, a different sort of dependency injection!) Or am I stuck making nuget packages or hacking around in AssemblyResolve events?

MSBuild: Remove Reference to Assembly added via NuGet

In the process of moving some legacy code from packages.config to PackageReference.
I have a NuGet package (let's say A.nupkg) that has a reference to a different NuPkg (B.nupkg). B.nupkg includes a reference to Foo.dll.
A project referenced A.nupkg in packages.config, but B.nupkg was not (despite being a transitive dependency). The problem is that the project references a drop-in replacement (same namespace and classes, but including bug fixes) for the Foo API in the form of a Foov2.dll
Now with the change to PackageReference the transitive dependency is picked up, Foo.dll is referenced by the project and we end up with ambiguous references between Foo.dll and Foov2.dll. I can't change the NuGet package (wish I could) so I need a workaround.
I tried adding a target that removes the unwanted reference before building it, but I can't find the right spot to put it - or maybe references from NuGets are handled different to normal references:
<Target Name="RemoveOutdatedReferences" BeforeTargets="BeforeBuild">
<Message Importance="High" Text="All references: #(Reference->'%(FileName)').
Sadly no Foo.dll so no wonder I can't remove it."/>
<ItemGroup>
<Reference Remove="Foo, Version=1.2.3.4, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb" />
</ItemGroup>
</Target>
Alternatively I also tried to remove the whole transitive NuGet package, but using <PackageReference Remove="Foo"/> didn't work either.
It appears like PackageReference Alias feature is designed specifically for scenarios of namespace collisions.
https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#packagereference-aliases
In some rare instances different packages will contain classes in the
same namespace. Starting with NuGet 5.7 & Visual Studio 2019 Update 7,
equivalent to ProjectReference, PackageReference supports Aliases. By
default no aliases are provided. When an alias is specified, all
assemblies coming from the annotated package with need to be
referenced with an alias.
According to our little discussion, the only option so far I see is to create a custom NuGet package which encapsulates A.nupkg without its dependencies:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<PackageId>My.Wrapper.Around.A<PackageId>
<PackageVersion>1.0.0<PackageVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="A" Version="x.y.z">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
According to the Microsoft docs, <PrivateAssets>all</PrivateAssets> should prevent all transitive dependencies from A.nupkg flowing up to the consumer.
And in your target project:
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="My.Wrapper.Around.A" Version="1.0.0" />
<PackageReference Include="Foov2" Version="1.0.0" />
</ItemGroup>
</Project>

.NET Core - build project specifying ReferencePath

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

Use Newtonsoft library in NetStandard 2.0 class library

I am developing a class library based on the NetStandard 2.0 framework for multiple platform compatibility sakes, and I need to serialize and deserialize objects. So I added a reference to the Newtonsoft library.
The problem is that I have the following exception at runtime:
System.IO.FileNotFoundException: 'Could not load file or assembly 'System.ComponentModel.Annotations, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.'
I tried to manually add a reference to the System.ComponentModel.Annotations version 4.2.0.0 but this version is not available.
Is there a way to use Newtonsoft with NetStandard 2.0, or an alternative to perform serialization/deserialization operations?
Update: it seems that adding a reference to System.ComponentModel.Annotations" Version="4.4.1" and rebuilding the solution fixed the problem.
Here is the content of my csproj file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.4.1" />
</ItemGroup>
</Project>
So I have been looking at referencing Newtonsoft.Json from the .NETStandard 2.0. It's all there and ready in version Newtonsoft.Json.11.0.2.
~/packages/Newtonsoft.Json.11.0.2/
Just reference it in csproj like so...
<Reference Include="Newtonsoft.Json">
<HintPath>..\APAS.WebInterface\packages\Newtonsoft.Json.11.0.2\lib\netstandard2.0\Newtonsoft.Json.dll</HintPath>
</Reference>
The solution of #user9200027 to add a reference didn't work for me.
However referencing as content does work, but it has the side effect of showing up in the solution explorer file list.
But note that if targeting multiple frameworks one should add a condition for the .net standard framework, otherwise it will override the file for the non .net standard frameworks as well.
Here is a sample .csproj entry:
<Content Condition="$(TargetFramework)=='netstandard2.0'"
Include="$(NuGetPackageRoot)\newtonsoft.json\12.0.2\lib\netstandard2.0\Newtonsoft.Json.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<Visible>False</Visible>
</Content>

Categories