Disclaimer: this only works for a small set of project types.
- class library projects
- console apps
- ASP.NET Core web apps
- .NET Core
Replace the contents of your csproj with the following, based on your project type:
Class library
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net46</TargetFramework>
</PropertyGroup>
</Project>
Console app
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net46</TargetFramework>
</PropertyGroup>
</Project>
Test project
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net46</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
</ItemGroup>
</Project>
Approach two: manually convert
Trim the fat manually.
Unfortunately, there is no magic button or command line tool to upgrade from VS 2015 to VS 2017 formats.

Keep your code in source control.
First line
Old
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
New
<Project Sdk="Microsoft.NET.Sdk">
Must delete
These lines must be removed to make the new format work:
<!-- usually at the top of the file -->
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<!-- usually at the bottom -->
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
Test projects usually have one or many blocks named <Choose>
. These must be deleted too:
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
</ItemGroup>
</When>
<Otherwise />
</Choose>
Must change
These lines must be changed:
Old:
<PropertyGroup>
<!-- ... -->
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>
New:
<PropertyGroup>
<!-- v4.5.2 => net452. v4.6 => net46, etc.-->
<TargetFramework>net452</TargetFramework>
</PropertyGroup>
That massive list of files
You can can delete any lines nested in an <ItemGroup>
that match with <Compile>
or <EmbeddedResource>
. This gets special mention, however, because their are excpetions.
By default, the new SDK will pick up default globbing patterns.
<!-- the defaults -->
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />
If you have anything outside of the project folder, you’ll need to keep those lines so VS and the compiler know where to find your code.
What to keep
These are common, non-default settings:
<PropertyGroup>
<Optimize>true</Optimize>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<OutputType>Exe</OutputType>
<!-- when RootNamespace != the csproj file name -->
<RootNamespace />ClassLibrary1</RootNamespace>
<!-- when AssemblyName != the csproj file name -->
<AssemblyName>ClassLibrary1</AssemblyName>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>$(DefineConstants);MY_CUSTOM_CONSTANT</DefineConstants>
<Platform>x64 or x86</Platform>
</PropertyGroup>
<ItemGroup>
<!-- keep references unless they are to package files. See the section about the NuGet upgrade below. -->
<Reference Include="System.Configuration" />
</ItemGroup>
Can remove

Heads up: these are suggestions for things you don’t need in most cases, but not all.
Project references
Old
<ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj">
<Project>{2C7DF870-5B35-49EF-963D-EE1E72C3177E}</Project>
<Name>ClassLibrary1</Name>
</ProjectReference>
New
<ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj" />
Also, MSBuild now does transitive project-to-project references automatically.
Example: Project A => Project B => Project C.
Old
Both B and C had to be listed.
<!-- in projectA.csproj -->
<ProjectReference Include="..\ProjectB\ProjectB.csproj">
<Project>{A900C843-8340-421B-B4F0-6C65A0D093C4}</Project>
<Name>ProjectB</Name>
</ProjectReference>
<ProjectReference Include="..\ProjectC\ProjectC.csproj">
<Project>{871AC142-FC46-49F5-A5E0-90436648B9C5}</Project>
<Name>ProjectB</Name>
</ProjectReference>
New
Only B needs to be listed. Project C will automatically be referenced as long as Project B references it.
<!-- in projectA.csproj -->
<ProjectReference Include="..\ProjectB\ProjectB.csproj" />
PropertyGroup
Bring out the red ink
Now the fun part, start deleting tons of stuff. The following elements under <PropertyGroup>
can be deleted.
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace />ClassLibrary1</RootNamespace>
<AssemblyName>ClassLibrary1</AssemblyName>
<Optimize>false</Optimize>
<ProjectGuid>xyz</ProjectGuid>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<FileAlignment>512</FileAlignment>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NuGetPackageImportStamp></NuGetPackageImportStamp>
<TargetFrameworkProfile />
<TestProjectType>UnitTest</TestProjectType>
<IsCodedUITest>False</IsCodedUITest>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
You likely also have duplicates of these properties under a PropertyGroup that looks like this:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
That whole block can usually be deleted too.
NuGet: imports and references
This gets special mention.
Old
In NuGet 2, you had a packages.config
file AND you had <Import>
and <Reference>
items in the csproj file.
<Import Project="..\..\packages\xunit.runner.visualstudio.2.2.0\build\net20\xunit.runner.visualstudio.props" Condition="Exists('..\..\packages\xunit.runner.visualstudio.2.2.0\build\net20\xunit.runner.visualstudio.props')" />
<ItemGroup>
<None Include="packages.config" />
<Reference Include="MySql.Data, Version=6.9.9.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
<HintPath>..\..\packages\MySql.Data.6.9.9\lib\net45\MySql.Data.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
New
Delete the packages.config file. Delete any <Reference>
and <Import>
items that refer to files from packages.
Replace them with <PackageReference>
.
<ItemGroup>
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
<PackageReference Include="MySql.Data" Version="6.9.9" />
</ItemGroup>
About transitive references
In NuGet 2, you listed every single package to be included. In NuGet 4 (VS 2017), you only need to list top-level dependencies. Everything else is that those top-level dependencies require will be imported automatically.
Old
<?xml version="1.0" encoding="utf-8"?>
<!-- in packages.config -->
<packages>
<package id="xunit" version="2.2.0" targetFramework="net452" />
<package id="xunit.abstractions" version="2.0.1" targetFramework="net452" />
<package id="xunit.assert" version="2.2.0" targetFramework="net452" />
<package id="xunit.core" version="2.2.0" targetFramework="net452" />
<package id="xunit.extensibility.core" version="2.2.0" targetFramework="net452" />
<package id="xunit.extensibility.execution" version="2.2.0" targetFramework="net452" />
<package id="xunit.runner.visualstudio" version="2.2.0" targetFramework="net452" developmentDependency="true" />
</packages>

Figuring this out will be trial and error. Best wishes if you have a long list.
New
<!-- in csproj -->
<ItemGroup>
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
<!-- Notice that xunit.assert, xunit.core, etc. are imported automatically.-->
</ItemGroup>