This is a series on the latest 2.* .Net Core bits, Following on from the original .Net Core Series

(At the time of writing, 2.1.4. I use windows, you don’t have to!)

Last time we created a new application. Once with dotnet CLI and once with Visual Studio.

Now we will have a look at the csproj file. Since I assume the Program.cs file and other code files are self-explanatory, I will give them a miss.

csproj

Let’s take a look at the MyNewLib.csproj file as a result of running dotnet new classlib:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

</Project>

This is not your typical csproj file of old. If you are at all familiar with what a csproj looks like for the full .Net framework, from Visual Studio 2015 and older, you will recall an unwieldy large file explicitly declaring everything.

You can relax, now those days are over. Yes, this is still MSBuild. Yes, you can still customise the same way you always have, everything you already know about MSBuild still applies. We just have some new targets that do some conventional magic, so you don’t have to. Yes, there are a few changes but we will get to those.

The main difference you will see is the <Project /> tag has an Sdk attribute, with the value "Microsoft.NET.Sdk". This is the part that tells it to use the new magic.

All your cs files will automatically be added to the project. You may need to manually add some Content files, Embedded files and the likes where you use custom copy to output options, or when you need custom Compile directives, Both advanced topics I will defer on, other than to just give a heads-up to those who know what I am talking about.

You can see the TargetFramework has been specified as netstandard2.0. And that is all it needs. More on TargetFramework a bit further down this page.

Next, we will take a quick look at the default console project, test project and web project files, as well.

A console app’s MyNewApp.csproj file with command dotnet new console

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>

</Project>

The default project settings are for a library, so nothing special is required when creating a library. But now we want an application we see an extra parameter defined, OutputType with a value of Exe. This is what tells the compiler you want it to compile an entry point into your application (Main). (Especially worth noting, what this is doing for you, since your Exe will still actually be a .dll in dotnet core.) We are also now targetting netcoreapp2.0 as the TargetFramework, so a concrete runtime version of netcoreapp2.0 rather than targetting a dotnet standard compatibility version of netstandard2.0 as we did with the library.

An xUnit project with command dotnet new xunit looks like this:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>

    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
    <PackageReference Include="xunit" Version="2.3.1" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
    <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
  </ItemGroup>

</Project>

An xUnit project doesn’t need an entry point since it is run by xUnit instead, xUnit provides us with an entry point. We still want it to be executable against a specific runtime version, so we target netcoreapp2.0 here as well. Lastly, we have IsPackable set to false. IsPackable is used by NuGet packaging, so we are setting the project up so that even if you try to dotnet pack this project, is will not create a package. You can add this and set it to true when you want to ensure a project does get packed.

We see this time an ItemGroup section as well. This is used as a grouping mechanism and can contain lots of different settings. Specifically, in this project, we see 3 PackageReference tags. These are NuGet package references. We also see a DotNetCliToolReference. This is for packages that are development-time dependencies, specifically executables that run against your project. Usually, these enable functionality to run dotnet mycommand from a package dotnet-mycommand. In this case, it gives us dotnet xunit.

And finally, an MVC web application looks like this from command dotnet new MVC:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" />
  </ItemGroup>

  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" />
  </ItemGroup>

</Project>

The only new thing here is the Sdk attribute on the <Project /> tag has changed to "Microsoft.NET.Sdk.Web". This sets up different MSBuild targets, and we get a slightly different set of defaults to ensure an AspNetCore application is built, which includes handling of some special directories and file types, like wwwroot, and Views containing those cshtml files.

Now we will dive a bit more into these tags and some others that you might find useful at a basic level.

Metadata

There is a bunch of metadata information you may or may not want to add to your projects. Mostly, this is useful when publishing as source, or packages (more on NuGet later). Some of this information carries over to the dll files produced.

  <PropertyGroup>
    <Authors>csMACnz</Authors>
    <NeutralLanguage>en-NZ</NeutralLanguage>
    <Company>csMACnz</Company>
    <Product>MyApp</Product>
    <Copyright>Copyright © csMACnz 2018</Copyright>
  </PropertyGroup>

Compiler Options

Those of you familiar with Compiler Warnings and Errors may be interested in the following available properties. These typically go in the main PropertyGroup tag.

  <PropertyGroup>
    <DebugType>portable</DebugType>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  </PropertyGroup>

You should also find most of the settings such as NoWarn, WarningLevel etc still work as expected (https://msdn.microsoft.com/en-us/library/jj715718.aspx).

Frameworks and dependencies

There are a few different references that we can use to reference other projects and packages. We have already seen two of these.

<PackageReference Include="xunit" Version="2.3.1" />

This references a NuGet package called xunit, version 2.3.1. This will resolve depending on how your NuGet configuration is set up. Likely it will come from nuget.org, but you can use a nuget.config file to change where it looks for your NuGet packages.

<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />

This is also a NuGet package reference, but will a particular purpose of being a tool available at build time, rather than code available at compile and package time.

<ProjectReference Include="..\MyNewLib\MyNewLib.csproj" />

This is a reference to another project.

You can add project and package references manually, or you can use the dotnet add package ... and dotnet add reference ... commands instead (see dotnet add -h).

TargetFramework(s)

The PropertyGroup property TargetFramwork determines the (you guessed it) Target Framework your application or library targets. There is a companion TargetFramworks that lets you input multiple targets (semicolon-separated) and will allow you to target one or all frameworks when you restore, build, run, and (later) pack or publish. If you specify multiple targets here, you will likely have to specify which target to run against later with dotnet run -f netcoreapp2.0 etc.
Both TargetFramwork and TargetFramworks support all the new (netcoreapp2.0, netcoreapp1.1, netcoreapp1.0, netstandard*) monikers, as well as full framework targetting (net45, net452, net46, net47 etc etc) as well. Note that to build against the full framework, the development SDK needs to be installed on the machine already, and cannot be pulled down like the new netcoreapp SDK versions can be.

more on csproj

For information on how project.json interacts with NuGet itself, head over to the NuGet 3.0, .NET and project.json page on nuget.org, which also has some format notes here. Notably, where there are differences between nuget and dotnet usages.

As we move forward into more complex examples, we will touch on other MSBuild parameters we can add and change in our csproj file.

If you are familiar with the previous project.json file, you may find it informative to read A mapping between project.json and csproj properties.

Much more detail then I have gone into here can be found at Additions to the csproj format for .NET Core.

global.json

{
  "sdk": {
    "version": "2.0.3"
  }
}

This is another magic file you may still find useful, to pin a project to a specific version of the .Net Core SDK. Especially useful since you can have multiple versions installed on the same machine

Some little information on global.json is found here.

If you are installing multiple versions, you may find this helpful, too. dotnet-install-script

Next up

We now have a bit of an idea of how the magic works. Going forward we will try out some common use cases to determine what changes to the csproj we need to make to achieve our results.