I have a Xamarin Forms application. I'd like to use the latest C# version in platform specific projects (i.e. Xamarin.iOS and Xamarin.Android). It looks like I can add <LangVersion>latest</LangVersion> to the .csproj files. However, I'm not sure where to add it exactly. I see a lot of PropertyGroup tags in the project files (usually one for each simulator and release type). Do I need to add it to every PropertyGroup? I need the latest language version to be available when debugging and in production.
Generally, if you use the latest version of Target framework, the version of C# will be the latest. Then you will not need to set the version of C# manually.
Right click the solution of Xamarin Forms, then click Properties, then can set the Target framework as follow.
You will refer to this official document to know its explains.
And also can set C# version manually in .csproj as follow:
<PropertyGroup>
<LangVersion>latest</LangVersion>
</PropertyGroup>
Here is the C# language version reference.
James Montamagno posted a tutorial on his Blog here: https://montemagno.com/use-csharp-8-everywehre/
You just have to change the <LangVersion /> value of your .csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Forms" Version="4.3.0.947036" />
<PackageReference Include="Xamarin.Essentials" Version="1.3.1" />
</ItemGroup>
</Project>
if you want to change the language of your platform specific code just add:
<PropertyGroup>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
to your ProjectName.iOS.csproj or ProjectName.Android.csproj.
Related
I want to support multiple TargetFrameworks in my Visual studio Microsoft.NET.Sdk project, but I want to make it so that I would have several solutions, where solution would determine which target framework it will compile.
Theoretically this can be done for example like this:
Directory.Build.props:
<Project>
<!--
TargetFramework is not defined at nuget restore phase - but we can define it.
If solution name contains "netcore" - then solution aims to .net core.
-->
<PropertyGroup Condition="$(SolutionName.Contains('netcore'))">
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="!$(SolutionName.Contains('netcore'))">
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<!-- nuget restore path must be different for different target frameworks, otherwise it will create mess between projects -->
<BaseIntermediateOutputPath>$(MSBuildThisFileDirectory)obj\nuget\$(TargetFramework)\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
</PropertyGroup>
</Project>
and then to have two solutions, one for .net framework 4.7.2, one for .net core, where second solution filename should contain 'netcore' string in it.
This however will not work pretty in case if TargetFramework starts to vary also between netstandard2.0 and so on...
Then it came to my mind - that maybe I could perform nuget restore for multiple frameworks, but just to compile project for one framework. I think BeforeBuild event could be used to differentiate build phase between nuget restore and build - so I wrote something like this:
test.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp3.1;net472</TargetFrameworks>
</PropertyGroup>
<Target Name="BeforeBuild">
<PropertyGroup>
<TargetFrameworks>net472</TargetFrameworks>
</PropertyGroup>
<Message Text="TargetFrameworks: $(TargetFrameworks)" />
</Target>
But this does not work, build happens for two frameworks, not for one, like I've desired originally.
Of course BeforeBuild should be eventually executed for solution, which does not have netcore in it's name.
Maybe someone could propose better solution for this one ?
Figured out.
So multiple solutions will not tolerate each other presence, and nuget restore directory needs to be overridden. Override can be done per solution basis, just to put things simple - just use solution name to define nuget restore path.
Directory.Build.props:
<Project>
<PropertyGroup>
<BaseIntermediateOutputPath>$(MSBuildThisFileDirectory)obj\nuget\$(SolutionName)\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
</PropertyGroup>
</Project>
And individual projects then determine target framework, which they want to use, for example like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<PropertyGroup Condition="$(SolutionName.Contains('netcore'))">
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="!$(SolutionName.Contains('netcore'))">
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<IntermediateOutputPath>$(SolutionDir)obj\$(TargetFramework)-$(Configuration)\$(AssemblyName)\</IntermediateOutputPath>
<OutputPath>$(SolutionDir)bin\$(TargetFramework)-$(Configuration)</OutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Remove="obj\**" />
<EmbeddedResource Remove="obj\**" />
<None Remove="obj\**" />
</ItemGroup>
</Project>
This will allow to have separate solutions, one for each target framework, but also will work correctly for nuget restore + build.
I have tried to read in Microsoft documentations as well in Nuget and Dotnet and was unable to understand the corresponding behavior.
When working on a C# project along with .csproj I have encountered the following,
Here is an example of what I want to achieve:
I am working for instance with the following .csproj file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectTypeGuids>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<Authors>Confluent Inc.;Andreas Heider</Authors>
<Description>Confluent's .NET Client for Apache Kafka</Description>
<Copyright>Copyright 2016-2020 Confluent Inc., Andreas Heider</Copyright>
<PackageProjectUrl>https://github.com/confluentinc/confluent-kafka-dotnet/</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/confluentinc/confluent-kafka-dotnet/blob/master/LICENSE</PackageLicenseUrl>
<PackageIconUrl>https://raw.githubusercontent.com/confluentinc/confluent-kafka-dotnet/master/confluent_logo.png</PackageIconUrl>
<PackageReleaseNotes>https://github.com/confluentinc/confluent-kafka-dotnet/releases</PackageReleaseNotes>
<PackageTags>Kafka;Confluent;librdkafka</PackageTags>
<PackageId>Confluent.Kafka</PackageId>
<Title>Confluent.Kafka</Title>
<AssemblyName>Confluent.Kafka</AssemblyName>
<VersionPrefix>1.4.3</VersionPrefix>
<TargetFrameworks>net45;net46;netcoreapp2.1;netstandard1.3;netstandard2.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>Confluent.Kafka.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="librdkafka.redist" Version="1.4.2">
<PrivateAssets Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">None</PrivateAssets>
</PackageReference>
<PackageReference Include="System.Memory" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="PeterO.Cbor" Version="3.5.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.Console" Version="4.3.0" />
<PackageReference Include="System.Linq" Version="4.3.0" />
<PackageReference Include="System.Runtime.InteropServices" Version="4.3.0" />
<PackageReference Include="System.Runtime.Extensions" Version="4.3.0" />
<PackageReference Include="System.Threading" Version="4.3.0" />
</ItemGroup>
</Project>
As you can see in the last ItemGroup there is a Condition=" '$(TargetFramework)' == 'netstandard1.3',
what I am trying to figure out is from where dotnet takes this argument $(TargetFramework) from.
By the way, it seems to be variable notation $(SomeVariable) (not only TargetFramework) and it would be great for me to understand from where and how these variables are being pulled from.
Any leads?
Thanks!
As there is already a very detailed answer on some mechanics, here are a few helpful pieces of information to know that can help detail the understanding of the behavior:
the "SDK=..." part imports things at the top and bottom of the project.
It is actually syntactical sugar for <Import> statements at the top and bottom ends of your project content. This means that logic by the SDK (and its deeper imports) can both set defaults that you can use in your project (automatic Sdk.props import at the top) and process values you set in the project (automatic Sdk.targets import at the very bottom).
<PropertyGroup> elemts are processed before <ItemGroup> elements.
MSBuild evaluates static content (i.e. not inside a <Target>) in a very specific order: Property Groups and Imports then Item Definition Groups and then Item Groups.
This means you can use $(FooBar) in an <ItemGroup> at the very top even if the <PropertyGroup> defining <FooBar> i at the very bottom.
The SDK itself uses this behavior - your $(TargetFramework) (singular!, if set. more on that later) is used in some property groups in the imported SDK targets to set TargetFrameworkIdentifier, TargetFrameworkVersion and TargetFrameworkMoniker - here's the code.
When the processing of <PropertyGroup>s is complete, this means that on the pass for <ItemGroup> elements, you can use $(TargetFrameworkIdentifier).
A similar mechansim is used for some item groups that the sdk has before your project content that can be controlled by properties set in the project (DefaultItemExcludes, EnableDefaultCompileItems etc).
TargetFrameworks (plural) projects actually build multiple times.
If a project has no TargetFramework but a TargetFrameworks (plural) property, it causes multiple sub-builds for which the TargetFramework property will be set. This means that everything is evaluated once for an "outer" build and then once per target framework for "inner builds". Here's the code
$(SomeVariable) is the syntax for resolving an MSBuild property. These are key-value pairs, which can be defined by environment variables, in arguments to MSBuild, by a <PropertyGroup> elements within an MSBuild file, and by MSBuild targets using the CreateProperty task.
TargetFramework is somewhat of a special property. If the project only builds for one target framework, then the property is set explicitly in the project file, e.g.:
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
In your case, your project can be built for multiple frameworks, as specified in the TargetFrameworks property (notice the plural), e.g.:
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;net462</TargetFrameworks>
</PropertyGroup>
When you build this project with e.g. dotnet, it actually builds the project multiple times, once for each target framework, by essentially setting the TargetFramework property via argument when calling MSBuild.
If you want to see behind the magic a bit, here are two ways to get very verbose information on what MSBuild is doing:
Use the -pp argument to have MSBuild resolve all the magic .NET Core-ish stuff, as well as all imported project files, into a single project file for you to inspect. This won't actually run the build.
Use the -bl argument to run the build, but log everything MSBuild does in a binary format. You can then view the .binlog file using this GUI.
Additional resources for MSBuild:
MSBuild documentation (written primarily from the perspective of the classic Windows-only .NET Framework)
Additions to the project file format made for "SKU-style" project files (used in .NET Core, .NET Standard, and .NET 5+).
I can't figure out how should I set up dependencies (where to add EntityFramework nuget packages) in this scenario:
Core.Persistence project which compiles to .NET Standard 2.0 DLL library. I have Entity Framework 6, database entity classes for EF, DbContext etc. It is supposed to depend just on EntityFrameworkCore.
Core.Domain project which also compiles to .NET Standard 2.0 DLL library. I want to put my business object POCO classes here. This is supposed to have no dependencies.
Core.Application project, this is .NET Standard 2.0 DLL library. I have all application logic here. It depends on Core.Persistence because it makes database queries and Core.Domain because it produces bussiness objects from query results.
Client.ConsoleClient project. It makes .NET Framework 4.7.2 executable. It is supposed to depend only on Core.Application, but I have a problem here.
Client.WindowsClient project which I don't want to focus in this question.
So, this is what I have done:
The problem is, that I'm getting System.IO.FileLoadException when I try to call method from Core.Application.
It says that it cannot find System.Interactive.Async file (which is dependency of EntityFrameworkCore). After I add this file as dependency - there are other System.IO.FileLoadException errors.
So, temporarily I have added EF6 Core NuGet package to my Client.ConsoleClient, and problems with System.IO.FileLoadException are gone, but I feel I'm doing something wrong.
At this moment I figured out, that Visual Studio is not copying DLL files from Core.xxx projects outputs into Client.ConsoleClient project output, and that's why I'm getting errors.
How to fix this properly?
This is a wellknown and quite old hurt logged on GitHub at:
dependencies don't flow from new NET Standard project to old desktop projects through project references link
A possible solution is to add the NuGet dependency to the Full NET Framework project, as you did.
The other suggestion to include the following to the .csproj project file of the Full NET Framework project is also working for me.
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
Note that I am using package references in the NET Standard projects.
As for now, it looks like NET Standard projects are best to be consumed as NuGet packages, as these would include any dependent references as NuGet packages into the target project.
Core.Persistence.csproj referencing Entity Framework
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.6" />
</ItemGroup>
</Project>
Core.Application.csproj referencing Core.Persistence
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Core.Persistence\Core.Persistence.csproj" />
</ItemGroup>
</Project>
ConsoleClient.csproj referencing Core.Application
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<!-- ... -->
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<!-- ... --->
<ItemGroup>
<ProjectReference Include="..\Core.Application\Core.Application.csproj">
<Project>{067b3201-3f65-4654-a0fb-e8fae521bf29}</Project>
<Name>Core.Application</Name>
</ProjectReference>
</ItemGroup>
</Project>
The new SDK format csproj files don't play terribly well with the legacy format project files.
But fear not as .NET Framework console apps can use the SDK format!
Make sure you have your work committed to source control, or make a copy of the folder, and then do the following:
Delete Properties\AssemblyInfo.cs from Client.ConsoleClient. We won't be needing this any more as the contents of that file now go into the project file.
Delete packages.config - again, Nuget references will be stored in the project file - that's if you need any Nuget references after we reference Core.Application later.
Open Client.ConsoleClient.csproj in a text editor and change the contents to:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
</Project>
Reload the project in Visual Studio.
Add a reference to Core.Application and add any Nuget packages you need.
If you had any content in Properties\AssemblyInfo.cs other than versions at 1.0.0.0, right click the project in Solution Explorer and click Package. Add the details you need and then save:
That's it, although there are 2 others things you might need to do depending on your circumstances:
If there are files which should be excluded, you'll need to exclude them, as the new project format includes all relevant file types by default.
You might have to set the language version. In my Visual Studio 2019 Preview, latest (latest minor version of C#) is the default so I don't need to do this.
Last year I added .NET Standard 2.0 support to the Network library. I did achieve this by creating a second (.NET Standard) project, and basically copy + paste the sourcecode. With some adjustments it was ready to go.
But since I add features on demand, it is really bothersome to change the same thing in both projects. It would be great to just create one code-base and simply change the compile target.
Pre-Compile statements aren't an option, because the .NET 4.x version does additionally include some NuGet packages, which aren't available for .NET Standard.
The solution I can currently think of is, to create a shared library, including all the cross-project classes. Or is there a much smoother solution?
Solved the Problem with the suggested solution. The .csproj Looks like following
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net46;netstandard2.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<Compile Remove="packages\**" />
<EmbeddedResource Remove="packages\**" />
<None Remove="packages\**" />
</ItemGroup>
<ItemGroup>
<!-- PackageReferences for all TargetFrameworks -->
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net46' ">
<!-- PackageReferences for net46 TargetFramework -->
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' ">
<!-- PackageReferences for standard2.0 TargetFramework -->
</ItemGroup>
</Project>
The only issue currently: I can't use the NuGet Package-Manager. I have to add every entry manually into the correct ItemGroup.
EDIT: The manual edit is only required if the packages are not supported by both TargetFrameworks. Simply Change in the Settings -> NuGet-Paket-Manager -> Default Format -> PackageReference
You can add source files from an existing project to another project as a link.
project a
somefile.cs
project b
Right click on project b, add existing item...navigate to somefile.cs in project a, and then add
You can edit the file from either project...so be careful.
This is a follow-up from this question.
As I'm not able to directly reference my .xproj in my .csproj in Visual Studio, I am forced to directly reference the built net461 dll. So in this situation, I want my .csproj to pre-compile the .xproj before running the .csproj itself.
I've tried modifying the .csproj manually and adding:
<ItemGroup>
<ProjectReference Include="..\SomeFolder\SomeProject.xproj">
<Project>{1A94B405-2D01-4A09-90D5-A5B31180A03B}</Project>
<Name>SomeProjectNamespace</Name>
</ProjectReference>
</ItemGroup>
But when I build the .csproj, it doesn't build the .xproj.
I won't be able to debug .xproj when I run my .csproj, which is bad enough... but at least when I run the .csproj I want to be sure I have the last version of my .xproj compiled.
xproj <=> csproj references in Visual Studio 2015 were always buggy and never worked well. As far as I know, there are no good workarounds.
That said, Visual Studio 2017 RC added support for building .NET Core and .NET Standard with csproj. This deprecates xproj and project.json.
With this new format, a project to project reference would look like this:
<!-- App.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net461</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Library\Library.csproj" />
</ItemGroup>
</Project>
<!-- Library.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
</PropertyGroup>
</Project>