TargetFramework vs. TargetFrameworks (plural) - c#

In the .csproj file in my .NET Core projects, there are these 3 lines by default:
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
Now if I want to target mulitple frameworks, I can change it to this:
<PropertyGroup>
<TargetFrameworks>netcoreapp2.2;net4.6</TargetFrameworks>
</PropertyGroup>
The difference is subtle, but it's there. When targeting multiple frameworks, you have to use <TargetFrameworks> (plural) instead of just <TargetFramework> (singular).
But why is it made like this? It seems like it would have been easier to just pick one of the two, and then always use that. Which leads me to the thought, that there might be a more complex reason, for choosing to different (although similar) words, depending on whether or not you target more frameworks. Can anyone enlighten me on the topic?

Basically when you want to target a single framework you use <TargetFramework> tag (in the case where you're building an app targeting .net-core), but it's possible also that you may conditionally reference assemblies multiple frameworks by using <TargetFrameworks> (in case you're building an app for both .net standard and .net-core) and then you can conditionally compile against those assemblies by using preprocessor symbols with if-then-else logic
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.4;net40;net45</TargetFrameworks>
</PropertyGroup>
<!-- Conditionally obtain references for the .NET Framework 4.0 target -->
<ItemGroup Condition=" '$(TargetFramework)' == 'net40' ">
<Reference Include="System.Net" />
</ItemGroup>
<!-- Conditionally obtain references for the .NET Framework 4.5 target -->
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<Reference Include="System.Net.Http" />
<Reference Include="System.Threading.Tasks" />
</ItemGroup>
</Project>
Source : https://learn.microsoft.com/fr-fr/dotnet/standard/frameworks

You can use TargetFrameworks configuration when you are building a class library that will runs well in .Net Full Framework and .Net Core Framework. This is the best use case to this configuration
It doesn't make sense use multiple target framework if you are building a website or webapi. In this case, use just one target framework.

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?

How to build a .net5 project with .net framework

I'm trying to build the same project with 2 different framework.
If relevant, the why : Because I need my project to be used as a reference in a .netcore project and a .netframework project, it has to be compatible with both to avoid duplicating the code.
I thought having build configurations would be the way to go, but the configuration manager makes an error stating the configuration is wrong when adding the framework build configuration.
My csproj looks like this
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">netcore</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">netframework</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
</PropertyGroup>
</Project>
But it's not working and I'm confused at to why or what the next step can be.
Any clue?
Your csproj looks really confused. It's not clear why Configuration, which is normally Debug or Release, has a netcore or netframework option, for example.
If you want to build for multiple target frameworks, use a single TargetFrameworks element, e.g.
<TargetFrameworks>net5.0;net451</TargetFrameworks>
If you use TargetFrameworks, do not also use TargetFramework.
You can use .NET Standard project. And have reference to it from both of the projects mentioned above. You can find more info here: .NET Standard
Quote from Microsoft themselves:
We recommend you target .NET Standard 2.0, unless you need to support an earlier version. Most general-purpose libraries should not need APIs outside of .NET Standard 2.0. .NET Standard 2.0 is supported by all modern platforms and is the recommended way to support multiple platforms with one target.

Which target framework should I choose for library project in .NET Core

My current mindset is to use .NET Standard x.x for libraries and concrete frameworks for executable projects. So with this in mind let's say I have one common (fictional) project Common.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net5.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
<PackageReference Include="FluentValidation" Version="9.5.4" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="FluentValidation" Version="8.6.0" />
</ItemGroup>
One library project Lib.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="Common.csproj" />
</ItemGroup>
And one executable project Cli.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="Lib.csproj" />
</ItemGroup>
So, in this case if FluentValidation library has different method signatures in different versions, compilation in some cases goes thru, but you get error at runtime.
What are the recommendations for cases like this? Should I just support all needed target frameworks in Lib.csproj?
I think that what you're after is using pre-processor directives with target frameworks:
#if NETSTANDARD
// Code targeting .Net Standard
#elif NET
// Code targetting .Net Core / .Net
#elif NETFRAMEWORK
// Code targetting .Net Framework
#endif
You can get as specific as you like i.e. targeting .Net Standard 2.0 exactly:
#if NETSTANDARD2_0
// .Net Standard 2.0 specific code
#elif
// anything else!
#endif
This will let Common.csproj use the correct method calls for the appropriate version of FluentValidation that you need.
Personally, I find it a little messy - but sometimes, needs must.

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>

How can I use ConfigurationPropertyAttribute in a .Net Core project targeting the net462 framework?

I'm trying to compile a small project, which was created with Visual Studio 2017 as a normal .Net project, using Visual Studio Code. One of the class, ConfigurationPropertyAttribute, cannot be found and I wonder which reference I should add to make it compile.
I tried searching for this class using reverse search in NuGet but it doesn't seem to exist.
Here is my .Net Core project:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net462</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="microsoft.extensions.configuration" Version="1.1.2" />
</ItemGroup>
</Project>
My ultimate goal is simply to be able to compile and debug an old project using Visual Studio Code instead of Visual Studio 2017 without necessarily using .Net Core as it is still lacking many features. I thought that by targeting net462 I would get access to everything from .Net 4.6.2 but it doesn't seem so. Did I miss something or is there something I didn't understand properly ?
Your .csproj file needs to add a Reference System.Configuration not a PackageRefrence Microsoft.Extensions.Configuration
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net462</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Reference Include="System.Configuration" />
</ItemGroup>
</Project>

Categories