Upgrading an existing .NET project files to the lean new CSPROJ format from .NET Core
If 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.
About Newsletter
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.
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.
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.
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.
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.
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.
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!
"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.
https://github.com/mungojam/Force.com-Toolkit-for-NET/blob/with-versions-cleared/src/ChatterToolkitForNET/ChatterToolkitForNET.csproj
Comments are closed.
Typo: Great a