Scott Hanselman

Upgrading an existing .NET project files to the lean new CSPROJ format from .NET Core

August 17, 2018 Comment on this post [15] Posted in DotNetCore
Sponsored By

Evocative random source code photoIf you've looked at csproj (C# (csharp) projects) in the past in a text editor you probably looked away quickly. They are effectively MSBuild files that orchestrate the build process. Phrased differently, a csproj file is an instance of an MSBuild file.

In Visual Studio 2017 and .NET Core 2 (and beyond) the csproj format is MUCH MUCH leaner. There's a lot of smart defaults, support for "globbing" like **/*.cs, etc and you don't need to state a bunch of obvious stuff. Truly you can take earlier msbuild/csproj files and get them down to a dozen lines of XML, plus package references. PackageReferences (references to NuGet packages) should be moved out of packages.config and into the csproj.  This lets you manage all project dependencies in one place and gives you and uncluttered view of top-level dependencies.

However, upgrading isn't as simple as "open the old project file and have VS automatically migrate you."

You have some options when migrating to .NET Core and the .NET Standard.

First, and above all, run the .NET Portability Analyzer and find out how much of your code is portable. Then you have two choices.

  • Great a new project file with something like "dotnet new classlib" and then manually get your projects building from the top (most common ancestor) project down
  • Try to use an open source 3rd party migration tool

Damian on my team recommends option one - a fresh project - as you'll learn more and avoid bringing cruft over. I agree, until there's dozens of projects, then I recommend trying a migration tool AND then comparing it to a fresh project file to avoid adding cruft. Every project/solution is different, so expect to spend some time on this.

The best way to learn this might be by watching it happen for real. Wade from Salesforce was tasked with upgrading his 4+ year old .NET Framework (Windows) based SDK to portable and open source .NET Core. He had some experience building for older versions of Mono and was thoughtful about not calling Windows-specific APIs so he knows the code is portable. However he needs to migrate the project files and structure AND get the Unit Tests running with "dotnet test" and the command line.

I figured I'd give him a head start by actually doing part of the work. It's useful to do this because, frankly, things go wrong and it's not pretty!

I started with Hans van Bakel's excellent CsProjToVS2017 global tool. It does an excellent job of getting your project 85% of the way there. To be clear, don't assume anything and not every warning will apply to you. You WILL need to go over every line of your project files, but it is an extraordinarily useful tool. If you have .NET Core 2.1, install it globally like this:

dotnet tool install Project2015To2017.Cli --global

Then its called (unfortunately) with another command "csproj-to-2017" and you can pass in a solution or an individual csproj.

After you've done the administrivia of the actual project conversion, you'll also want to make educated decisions about the 3rd party libraries you pull in. For example, if you want to make your project cross-platform BUT you depend on some library that is Windows only, why bother trying to port? Well, many of your favorite libraries DO have "netstandard" or ".NET Standard" versions. You'll see in the video below how I pull Wade's project's reference forward with a new version of JSON.NET and a new NuUnit. By the end we are building and the command line and running tests as well with code coverage.

Please head over to my YouTube and check it out. Note this happened live and spontaneously plus I had a YouTube audience giving me helpful comments, so I'll address them occasionally.

LIVE: Upgrading an older .NET SDK to .NET Core and .NET Standard

If you find things like this useful, let me know in the comments and maybe I'll do more of them. Also, do you think things like this belong on the Visual Studio Twitch Channel? Go follow my favs on Twitch CSharpFritz and Noopkat for more live coding fun!


Friend of the Blog: Want to learn more about .NET for free? Join us at DotNetConf! It's a free virtual online community conference September 12-14, 2018. Head over to https://www.dotnetconf.net to learn more and for a Save The Date Calendar Link.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
August 17, 2018 11:26
A badly needed/timely article!

Typo: Great a
August 17, 2018 14:34
Have just been doing a lot of this as part of upgrading to VS2017. I also recommened creating a new project, as the project migrater tool does a pretty good job but leaves a lot of cruft behind that is not necessary. My approach is to first make a copy of the solution in Explorer, then replace the project you are upgrading with a brand new empty project with the same name. Then install the needed NuGet packages, then just drag-n-drop the code files in from the backup copy you made. Thanks to wildcard includes they automatically become part of the project. Remember to set custom build actions such as embedded resources.

Note that when you install the NuGet packages you get the opportunity to de-cruft them. Old NuGet flattens NuGet dependency trees, but new NuGet doesn't. So if you have package A which depends on pacakges B, C and D which in turn depends on packages R, S, and T; in the new world you just need to install A and will have one PackageReference in the csproj rather than many.
August 17, 2018 15:55
Is there any way to get rid of the entries that Visual Studio still adds to the csproj file?

For instance our .net core web app project csproj file still has over a 1000 lines from all the ItemGroup Content inclusion and exclusion entries for javascript files and cshtml files.
August 17, 2018 16:19
My main issue with migrating to .NET Core/Standard right now is that it's not possible to hava a solution with mixed projects using old packages.config and packages in .csproj.
August 17, 2018 19:24
Fun to see you do this live. Would have been great to have Wade join in too. This could be a fun little series to do.
August 17, 2018 20:14
Scott: You should mention that this new project format still supports targeting full .net framework, you don't have to move to .net core/standard to benefit. Just use <TargetFramework>net462</TargetFramework> (or whatever version is appropriate) instead of <TargetFramework>netcoreapp2.1</TargetFramework>. The property pages might not let you do this, but you can in the xml.

Community: please refer to this new project format as "sdk-style project": https://github.com/Microsoft/msbuild/issues/3341
This helps everyone search for information.

Paul: You can add all javascript/cshtml files by deleting those <Content Include="Somefile.js"/>, and replacing with <Content Include="**/*.js"/>. Be aware that will include all files under your project root, which might include files that are currently explicitly excluded. So, you might instead want to <Content Include="wwwroot/**/*.js"/> to only capture the files under your web content root. By doing this, you won't have to update your csproj every time you add a new .js or .cshtml file.
August 18, 2018 4:48
Great. Now onto replacing the web.config file with a config file using a non-XML, non-JSON, non-nested structure format like key-value pairs (and no sections based on custom c# to XML serializers like the ones built into web.config).

Reducing the many ways of doing the same thing in the project file, config file, web api routing, controller method by convention pathing, c# implied private properties, etc would greatly help.
August 18, 2018 9:30
I performed this conversion on my company's entire set of internal libraries using the tool mentioned above. Tip: Leverage source control in case the migration doesn't go well!

We are still targeting .NET 4.6.1-4.7.1 using the new csproj format. We aren't running on Core or Standard yet.

There are 'gotchas' (like with install.ps1 in nuget packages being non functional) but they are documented on GitHub.

If the full migration to this new project format is too much for your codebase you can try just migrating from packages.config to PackageReference which has worked for us even in Web Forms apps.
August 18, 2018 23:12
HvanBakel welcomes issues and contributions so if you come across anything let him know or jump into the code. I've finally raised the issue about app.config that shows up in the video every time.

There is also a nuget package, which is handy if you are converting lots of projects and want to add any custom pre or post transformations to the project. Also useful if you are using another source control system that needs a check-out step as it can take that as an option.
August 19, 2018 14:43
I've just spent the last week doing this for > 150 projects.

Make sure before you convert your csproj file, that you click the "show all files" button in Solution Explorer to check for folders / files that have been excluded, as these will get included via globbing once converted and you'll waste time wondering why the project won't compile because of this random file with invalid references...

It's worth digging around the Common Project System GitHub repo - there are some interesting documents in there: https://github.com/dotnet/project-system

The big issue is the lack of Common Project System support for "classic" ASP.NET / WebAPI projects. This has been where the majority of the pain has been - enabling support for NetStandard, lots of issues with System.Runtime version mismatches, which I managed to solve via adding this to the Web.config:


<compilation debug="true" targetFramework="4.7.1">
<assemblies>
<add assembly="netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />
</assemblies>
</compilation>

and

<Reference Include="netstandard" />


to the csproj.

I've also not found a good solution for mixed VSTS builds where a solution has NETStandard or CPS class libraries, but a "classic" web application that uses MSBuild. If you have classic Web Apps, it's worth creating a new project and then diffing the web.config files because there are new compiler providers:


<compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider"/>


A shameless plug, but one of the scripts that helped me rationalise the references in CSPROJ files, app & web.config files, were the PowerShell scripts (use at your own risk!) on my blog post Reduce Merge Conflicts in .NET Solutions with PowerShell based Git Hooks, which sort references into alphabetic order, which made it much simpler to run diff tools against.

The new transitive dependency support is amazing. Using the "Show Project Dependency Diagram" option from the Solution Explorer menu is a good tool for working out how to start at the bottom of the dependency tree of your project and work upwards.

Converting all these projects has been a painful yet rewarding process, but it's left me wondering why on earth this wasn't the default approach since .NET 1.0!
August 20, 2018 22:22
Now you should show what this is like for a solution with several dozen projects spanning nearly every significant type of project over the past 15 years. It's not nearly so simple. At least we can do it now - initially we jumped on the .Net Core bandwagon a little early. We spun up a couple new .Net Core web apps that needed to live in the same solution and it caused a litany of dll hell issues that were time consuming, error prone, fragile, and difficult to sort through. Once <PackageReference /> became available in older MVC apps we were finally able to straighten a lot of that out.
August 21, 2018 4:02
"You WILL need to go over every line of your project files"
"Try to use an open source 3rd party migration tool"
Visual Studio definitely needs an integrated and officially supported tool to automate this kind of csproj migration as far as possible.
The approach showed in this article is great to do once for self education. Knowing our stuff is important. But it for sure does not scale at all to our existing collection of >100 csproj grown in the last 15 years. Full of legacy from .Net remoting over Silverlight to WCF.
The constant lack of a practicable ASP "classic" to Core migration story has painted ASP Core into a corner: For green fields, companies prefer an established web stack. For existing ASP .Net stuff the migration effort is to hight or it's even impossible (in our case due to WCF host) without completely rewriting major parts. That's a pity for the great technology ASP/.Net Core actually is.
August 21, 2018 10:01
Thank you for any other informative blog. The place else may I get that kind of info written in such a perfect approach? I have a mission that I am just now working on, and I have been at the look out for such info.
August 21, 2018 15:45
Now you have undercuts ;)
August 23, 2018 22:02
We're actively working on improving @HvanBakel's CsProj converter. Our current pre-release generates much neater project files for the example in this post though there is still more to do I believe:

https://github.com/mungojam/Force.com-Toolkit-for-NET/blob/with-versions-cleared/src/ChatterToolkitForNET/ChatterToolkitForNET.csproj

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.