Use Newtonsoft library in NetStandard 2.0 class library - c#

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>

Related

.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>

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.

Microsoft.AspNetCore.Mvc.ViewFeatures version mismatch (asks for 3.0.0, but the latest version is 2.2.0)

I get this error when I'm building my ASP.NET Core MVC application:
Error CS0012 The type 'Controller' is defined in an assembly that is
not referenced. You must add a reference to assembly
'Microsoft.AspNetCore.Mvc.ViewFeatures, Version=3.0.0.0,
Culture=neutral, PublicKeyToken=adb9793829ddae60'
And when I go to NuGet site, I see that the latest version of Microsoft.AspNetCore.Mvc.ViewFeatures is 2.2.0.
What should I do?
I had the same problem in my Class Library project. I found a solution by adding the following code to the project's .csproj file. I hope it works for you, too.
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
ASP.NET Core applications should use the web SDK. If you create a new ASP.NET Core MVC project, then inspect the project file, you'll see it has Sdk="Microsoft.NET.Sdk.Web". If yours just says Microsoft.NET.Sdk, you might want to change it.
If you're not using the web SDK for a good reason (for example, your project is a class library, not an application/exe), then given that the version number it's complaining about, it's clear you're using .NET Core 3.0. In 3.0 and higher, framework libraries are no longer distributed as NuGet packages, but instead use a new versionless FrameworkReference MSBuild item. Once everything targets .NET Core 3.0 and higher a LOT of package versioning issues are going to disappear.
Anyway, the ASP.NET docs page has a good example on the page for migrating from 2.2 to 3.0. There's also a little more information on FrameworkReference.
Here's the example I like that shows the difference between 3.0 and earlier versions:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;netstandard2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.0'">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.0" />
</ItemGroup>
</Project>
You have probably updated a nuget or referenced something that causes the mismatch.
open your solution go to the project
check references -> Microsoft...ViewFeatures
right click check properties window, you should see version there
the problem can be that some dll requires a higher version of this package
consolidating/updating some nuget that delivers this package as well might help, the dll/package might have been put into another nuget instead of shipping it separately

How to Multi target a library project with WPF controls

I have a class library project that needs to target .NET 3.5 and .NET 4.0, and the way it is done now is the typical way of creating separate projects per target framework and linking files in each project to the same source.
I would like to take advantage of the new csproj format that has come out with .NET Core projects because multitargeting is much simpler with the new csproj format.
I created a new Class Library (.NET Core) project and started to try porting my existing library over.
I don't really need to target .netcoreapp2.0, so my target frameworks look like this
<PropertyGroup>
<TargetFrameworks>net35;net40</TargetFrameworks>
</PropertyGroup>
and I have the following block of code to help with the .NET 3.5 oddities with the new csproj format.
<PropertyGroup>
<FrameworkPathOverride Condition="'$(TargetFramework)' == 'net35'">C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client</FrameworkPathOverride>
</PropertyGroup>
So far so good. Where things started going downhill is the fact that my class library has WPF Controls. I was getting compile errors because it couldn't find System.Windows and other WPF related items.
I found I could add references to other windows assemblies, so I added the following
<ItemGroup>
<Reference Include="PresentationFramework" />
<Reference Include="PresentationCore" />
<Reference Include="WindowsBase" />
</ItemGroup>
This got rid of most of my errors, but now I am getting errors like The name 'InitializeComponent' does not exist in the current context
Some WPF items migrated to a new library System.Xaml starting at .NET 4.0
The error The name 'InitializeComponent' does not exist in the current context is being thrown only when the .NET 4.0 target is being built.
To fix this, the following block needs to be added to the csproj file
<ItemGroup Condition="'$(TargetFramework)'=='net40'">
<Reference Include="System.Xaml" />
</ItemGroup>
Also, the xaml pages need to be built as a page, so the following also needs to be added to the csproj file
All xaml files that need to be compiled as page.
<ItemGroup>
...
<Page Include="Path\to\SomeWindow.xaml" />
<Page Include="Path\to\SomeOtherWindow.xaml" />
...
</ItemGroup>
This will remove the xaml files from your solution explorer, so a workaround was found here that adds the following blocks to get xaml pages built but still showing up in the Solution Explorer.
<ItemGroup>
<Page Update="#(Page)" SubType="Designer" Generator="MSBuild:Compile" />
</ItemGroup>
<ItemGroup>
<None Include="#(Page)" />
</ItemGroup>

Categories