Converting a big project to new VS2017 csproj format

Visual Studio 2017 introduced a new .csproj file format for C#  project that is the key to move to .NET Standard but it is useful also for project with Full Framework, because is more human manageable.

The main drawback of this approach is that you end compatibility with older version of VS, if you open the .csproj with VS2015 you are not able to compile the project anymore. For this reason, switching to new format should be a decision that is well discussed in the team.

New project format is really human friendly, the only drawback is losing compatibility with older VS.

Luckily enough, when you have a solution with 68 project, you do not need to do all the work manually thanks to a nice tool found on GitHub, CsprojToVs2017. I’ve done manual conversion in the past, it is time consuming and frustrating, the above tools is not perfect but it does most of the work for you.

First of all I’ve run the tool to do a conversion of the entire solution without creating backup files (why should you take a backup when you can do all the test in a Git branch isolated from the rest of the code?).

csproj-to-2017 --no-backup .\mysln.sln

The tool output lots of information, warning and other suggestion, I simply stored in a file as reference but I immediately opened the solution to verify that it compiles successfully and the answer is no, but most of the work was already done for me.

Automated tool can do most of the work for you, but you will always do some manual work to finish and fine tune the conversion.

First of all the tool remove all the information of assembly from assemblyinfo.cs file, and in my situation it generates an error because I have AssemblyInformationalVersion in my assemblyinfo.cs, value of this attribute is “localCompile” and here what the tools generates.

image

Figure 1: Incorrectly Version generated by conversion tool

I still prefer all version information to be stored inside assemblyinfo.cs, so I removed all the version information from all .csproj file, added the attribute <GenerateAssemblyInfo>false</GenerateAssemblyInfo> to avoid automatic generation of assemblyinfo and finally reverted all the modification done to all assemblyinfo.cs to revert to previous content.

Then I start having a strange error:

Assets file …\obj\project.assets.json' not found. Run a NuGet package restore to generate this file

This error can be solved simply running a dotnet restore and in this particular situation it probably happens because in the sln file there are still a couple of web project that were not converted because web project still should use the old format. If we removed those two project from the solution everything compiles correctly, probably VS2017 got confused when a solution mixed the two types of project. The funny thing is that this error did not happened to every member of the team and happens only when we switched branch from a branch with the old format to the new format. This is usually not a big problem, when we switch branch we should only issue a git clean –xdf to clean every file not in source control, dotnet restore and we are ready to go. Now that we do not have active branches with the old format, this issue is gone away.

Then we start having some compilation errors because the new project formats includes automatically all .cs files inside project folders and during the years, some files were probably removed from the project , but they are still in the file system as you can see in following picture, where we have in the left the converted project to the right the original one.

image

As you can verify the file ConfigurationManagerProcessManagerConfiguration is present in file system but it is excluded from the project (right part of the picture) and it is incorrectly included by the new project format.

To solve this problem you should open side by side converted version of the project and original version and manually remove all cs files that are still in source code but are not referenced by the old project format, to avoid them to be incorrectly included after conversion.

One nice side effect of the conversion is that we found a bunch of files still in source control but not included in the solution

Then we have problem with Post Build Action, because we use Pre and Post build action to xcopy some files around but with the new format it seems not to work anymore. It turns out that in post build and pre build action some of the properties, as $(ConfigurationName) were not correctly populated, thus all the post and pre build actions generate error. The solution is really simple, convert them to a standard MsBuild Target as seen in the following snippet, where you can see the correct way to use xcopy and the original code that gets converted.

image

Conversion is really simple, original part is commented in the lower part of previous snippet, and it is composed by a series of xcopy command inside a PreBuildEvent. To made it work with the new csproj format the solution is adding a Target with a BeforeTarget=”PreBuildEvent” that will be run before the pre build event, then add a Exec task for each xcopy instruction. Remember also that the output folder is not bin/debug but you need to append the net461 (framework version).

The whole conversion took almost a couple of  hours for a 68 project solution and thanks to the automatic conversion tool, most of the work was automated, we only need to tweak a little bit project files and solves some problems due to the fact that this is a 5 years old solution.

This conversion forces also you to review your build in VSTS or whatever build system you are using. I encountered a couple of modification to do to every build.

Once you switch to the new project version it is time to check the all automated builds.

  1. First of all I need to be sure that the version of NuGet used to restore dependencies should be set at minimum to 4.6. to be sure of the NuGet version used, you should use the couple of task  NuGet Tool Installer plus Nuget, as I described in a previous post.

image

If you are using a wrong nuget version, probably the build solution will fail with an error like: Assets file ……\project.assets.json’ not found. Run a NuGet package restore to generate this file.

Another problem is due to switching between a branch that still uses old project format and branch that use the new format. It is imperative that obj folders are cleared because building again, or the build probably will fail. To accomplish this, I simply decide to clear everything before a get sources. (This was related to the issue I explained before.)

image

Symptoms of not clearing the obj folder are weird error like: Your project file doesn’t list ‘win’ as a “RuntimeIdentifier”. You should add ‘win’ to the “RuntimeIdentifiers” property in your project file and then re-run NuGet restore.

The overall process went smooth, and now we are ready to start the migration to dotnetcore.

Gian Maria.

Configure Visual Studio 2017 15.5 for pull –rebase

I’m a great fan of rebasing over merge and I’m convinced that the default pull should be a fetch and rebase, using fetch and merge only when it is really needed. Not having the option to configure a GUI to do a pull –rebase is a really annoying problem, that can be somewhat limited configuring pull.rebase git option to true, as explained in previous post. Actually, the lack of rebase on pull option makes me stop using the IDE.

To have a linear history in Git, always consider rebase over merge, especially for everyday pulls.

I’ve used this technique extensively with VS 2017, the only drawback is an error during pull because VS complains about “unknown merge result” since he was not able to find merge commit. Give this, I was always reluctant to suggest to customers, because it is not good having your IDE constantly show error at each pull.

After updating to 15.5 version I noticed that the error went away and the IDE correctly tells me that a pull with a rebase occurred. If I open the global or repository settings in Team Explorer I can found that now, finally, pull.rebase is supported.

image

Figure 1: Pull with rebase is now supported, as well as other interesting options.

Actually these are the basic settings of Git, if you configure the repository or globally the Rebase local branch when pulling, it will set pull.rebase to true, nothing more. The important aspect is that the IDE now honor the settings. Suppose you have one local commit and one remote commit like in Figure 2:

image

Figure 2: Classic situation where you have local commit and remote commits

Now if you simply press the pull command in the IDE, you will see that VS is correctly issuing a rebase. When everything is finished you are informed that indeed a rebase was done, the error went away.

image

Figure 3: Visual  Studio correctly rebased local branch on the remote branch.

With this latest addition I can confirm that Visual Studio is now a really interesting IDE to work with Git (even if, if you are experienced with Git, probably you will still stick in CommandLine most of your time).

P.s: Another nice addition is the support to prune after fetch and support to push –force. If you try to push a commit after an amend, instead of the standard error, you will be prompted with a  MessageBox that asks you if you really want to force the push.

image

Figure 4: MessageBox asking for a push force if needed.

If you are curious about why VS is using a –force-with-lease instead of a standard –force, please read this article. Basically it is a way to force the push if no one had pushed something else in the meanwhile. If you really need to force a push, like when you rebase a feature branch, you can always use commandline.

Gian Maria.

Configure Git repository for automatic pull –rebase

I’m not a great fan of Git Graphical User Interfaces, I use mainly command line, but I needed to admit that, for novice user, the ability to use a GUI is something that can easy the pain of transition to a new tool. Visual Studio 2017 is a decent GUI for Git and since .NET developers are used to it, people want to stay as much as possible inside the IDE, leaving the commandline only for special operation (squash, reflog, etc)

The main problem I found with VS 2017 is the “pull” button, because I’m a great fan of pull –rebase instead that normal pull, because the history will be clearer. Suppose you have this situation:

image

Figure 1: Situation before a pull, one local commit, one remote commit

This is a standard, a developer has created a local commit and we have another commit done in the origin/master branch. This is what the develop see in Visual Studio sync interface:

image

Figure 2: The same situation of Figure 1 as seen in Visual Studio

From the comment you can see that the incoming commit is a simple add of readme.md file , this is unrelated to the modification done by developer, but if he press the pull button here is the result.

image

Figure 3: A merge commit is created due to the pull operation

If everyone will use the default pull option, the team will create a lot of unnecessary merge commits, thus I really prefer the pull –rebase approach, but the problem is: in Visual Studio there is no easy way to issue a pull –rebase and the pull button is just to easy to press to convince people to go to command line and issue a pull –rebase. Luckily there is an option that you can configure globally and that will default pull operation to do a rebase instead that a merge

git config –global pull.rebase true

After this configuration was done, whenever you issue a git pull, the –rebase option will be added automatically for you. Since VS 2017 honors git settings, if you press the pull button you got the right behavior, even if it is complaining that the merge result was unknown. (a rebase happened, not a merge)

image

Figure 4: After the pull, VS is complaining because the merge result is unknown, because a rebase was instead done

image

Figure 5: Indeed GitViz confirm that the pull operation was a pull –rebase

You can just ignore the error in Visual Studio and accept the fact that now, whenever you press the Pull button in Visual Studio you will trigger a pull with a rebase instead that a pull with merge.

This technique is not going to work with older version of Visual Studio, because they used the LibGit2Sharp library.

Gian Maria.

Unable to debug dll source code with symbol server

I’ve blogged in the past about using a Symbol server and I recommend to all people to use symbol servers whenever possible, to helping people troubleshooting problem on dependencies. Basically with a symbol server you can reference a dll in your project, but you can debug original source code as if you have the original project linked instead of having the dll.

Sometimes this process just don’t work. Recently I’have a customer that had problem with this scenario, and the real strange stuff is: I’m able to step in dll source code without problem from any machine, but noone of the customer’s developers are able to make it work. After I’ve sent them detailed instruction it worked, and we were able to track down the problem.

Visual Studio has a nice option to cache symbols in local directory to avoid downloading each time from the server. Here are my usual settings.

Visual Studio options to use local folder as a symbol cache

Figure 1: Visual Studio symbols settings

Developers in customer sites decided to use a subfolder of %TEMP% directory and this was the cause. As soon as they moved symbol cache to something like c:\symbols everything starts working. The underling cause is probably due to long paths.

If you have problem using symbol server, try using a really short path for your Symbols Local Cache directory.

In this specific situation we are using free symbols server in conjunction with MyGet nuget package feeds. In my machine here is the location for a source file during debugging.

Z:\Symbols\src\pdbsrc\MyGet\alkampfer\11111111-1111-1111-1111-111111111111\BIGEND\GianMariaRicci\Jarvis.Framework.Kernel\2C5AB5CBD1C74688974B2DDB55F51EDA1\Jarvis.Framework.Kernel\ProjectionEngine\ConcurrentProjectionEngine.cs

Usual %TEMP% variable is something like c:\users\gianmaria.ricci\appdata\local\temp (this is my system and it is long 44 characters), so it is not a good idea to use it for symbol source cache.

Since it is really easy to have really long path for your source when you use a symbol server, it is always a good idea using a short path for symbols cache directory, something like x:\SymSrc is probably the best solution.

If this does not solves your problem, another suggestion is using Fiddler to inspect the traffic between your Visual Studio and the Source Server to understand what is happening.

Gian Maria.

Quickly run Unit Test With VS 2012/2013/..

With VS 2012 and newer versions we can run Unit Tests from various frameworks directly from Visual Studio IDE, thanks to the concept of Test Adapters. When you are doing Test Driven Development you usually go with Red/Green/Refactor workflow; what you need is a way to quickly run all or part of your unit tests after you modified the code. The quickest solution is using the option to Run Tests After Build but it is available only for Premium and Ultimate edition, but you can also run test with little manual intervention resorting to Keyboard Shortcut.

Simply go to TOOLS->Customize menu, then choose to customize keyboard.

image

Figure 1: Keyboard customization in Visual Studio

Once configuration window is opened, search for TestExplorer.RunAllTests command, place cursor in the “Press shortcut keys:” textbox and press a shortcut, then press “assign” button to assign to this command. In my standard configuration I like to have CTRL+SHIFT+ALT+A shortcut, because it is not assigned to any other command and it is easy to press with left hand.

image

Figure 2: Assign CTRL+SHIFT+ALT+A to TestExplorer.RunAllTests command

Now I can write code, use shortcut and VS will build solution and run test for me automatically, without leaving my hands from the keyboard. Thanks to the various test adapter and the various grouping and filtering possibility offered by Test Explorer, you can do TDD in Visual Studio without the need of third party tools.

Gian Maria.