Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorted order of files in .csproj

In a Visual Studio csproj, the files to compile are referenced like this:

<ItemGroup>     <Compile Include="C\Something.cs">      <Compile Include="B\SomethingElse.cs">      <Compile Include="A\YetSomethingElse.cs">  </ItemGroup> 

It seems to me that the order is random (at least I cannot see an ordering principle).

It happened a few times that during fixing of merge conflicts, I mistakenly added a file twice (since there are a lot of files, and the file on the line of the merge conflict was at another position in the list already). This would be easy to avoid if there was a way to just sort the Compile Included files alphabetically.

Is this possible already (or do I have to write a script myself)? Are there any side effects I would have to be aware of?

like image 944
Wilbert Avatar asked Dec 13 '13 09:12

Wilbert


People also ask

Does order matter in Csproj?

Yes, you can just merge it. The order makes no difference.

How do I edit a .csproj file in Visual Studio?

Right-click on the project (tagged as unavailable in solution explorer) and click "Edit yourproj. csproj". This will open up your CSPROJ file for editing. After making the changes you want, save, and close the file.

What program opens Csproj files?

CSPROJ files are are meant to be opened and edited in Microsoft Visual Studio (Windows, Mac) as part of Visual Studio projects.


1 Answers

I have just come across this problem as more members of our team commit without their solution file, we add the file to the solution independently, they belatedly commit their solution file and Team Foundation Server/Visual Studio Online completely fails to get the merge right.

Some files added twice, some not added at all, everyone with a file in a different order - merge hell.

I have sort of worked around the inability to sort the Content files within a solution using the following C#, which I execute in the awesome LinqPad.

var documentsLocation = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);  var unsortedFilePath = Path.Combine(documentsLocation.ToString(), "UnsortedConfigItems.xml");  var unsortedFile = new FileInfo(unsortedFilePath);  if (!unsortedFile.Exists) {     throw new FileNotFoundException(string.Format("The unsorted file could not be found at path {0}", unsortedFilePath)); }  var sortedFilePath = Path.Combine(documentsLocation, "SortedConfigItems.xml");  const string RootNodeName = "ItemGroup";  const string IncludeAttributeName = "Include";  var contentElementNames = new [] { "Compile", "Content", "None" };  var xmlFile = XDocument.Load(unsortedFile.FullName);  if (xmlFile == null || xmlFile.Root == null || xmlFile.Root.Name != RootNodeName)  {     Console.WriteLine("Invalid file or unexpected file schema");     return; }  var allElements = xmlFile.Root.Elements();  var contentElements = new List<XElement>();  foreach(var elementName in contentElementNames) {     contentElements.AddRange(xmlFile.Root.Elements(elementName)); }  var elementsWithInclude = contentElements.Where(ce => ce.Attribute(IncludeAttributeName) != null && !string.IsNullOrWhiteSpace(ce.Attribute(IncludeAttributeName).Value));  if (!elementsWithInclude.Any()) {     Console.WriteLine("No content elements to sort");     return; }  contentElements = null; // Make candidate for garbage collection  var uniqueElements = new List<XElement>(elementsWithInclude.Count());  foreach (var element in elementsWithInclude) {     if (!uniqueElements.Any(e => e.Attribute(IncludeAttributeName).Value == element.Attribute(IncludeAttributeName).Value))      {         uniqueElements.Add(element);     } }  var sortedUniqueElements = uniqueElements.OrderBy(ue => ue.Name.LocalName).ThenBy(ue => ue.Attribute(IncludeAttributeName).Value).ToList();  var remainingElements = new List<XElement>();  foreach (var element in allElements.Where(ae => !elementsWithInclude.Contains(ae))) { // This uses elements with include and not sorted unique to avoid re-adding filtered files     remainingElements.Add(element); }  var sortedFile = new XDocument(); sortedFile.AddFirst(new XElement(RootNodeName));  sortedUniqueElements.ForEach(sue => sortedFile.Root.Add(sue));  remainingElements.ForEach(re => sortedFile.Root.Add(re));  sortedFile.Save(sortedFilePath); 

I'm sure it could be compressed slightly and made more elegant, but it does the job for us.

Steps for completeness-

  1. Make sure your project file is backed up, either in Source Control or locally.

  2. Right click on the Project in Solution Explorer and select "Unload Project" (if the Project file isn't shown and you only have a single Project in the solution then select "Always show Solution" in Visual Studio options).

  3. The project icon should have changed and it will be listed as "(unavailable)". Right click again and select "Edit ProjectName.csproj"

  4. Locate the ItemGroup node containing the content references and either cut or copy and paste into a new file (NotePad++ is ideal for this, but use the editor of your choice) and save at the location of XmlPathName. Ensure that the "ItemGroup" node is the root node of your new XML document.

  5. Alter the path in the SortedPathName if wanted, and ensure that your Windows user account has write permission to that location.

  6. Run the above C# in LinqPad (or branch it and create your own console application/Powershell script).

  7. Inspect the contents of the output file to make sure it looks correct (broadly consistent line count and format) and if possible validate as valid XML (the XML tools in Notepad++ is ideal for this).

  8. Copy the contents of the output file back to your solution file, replacing the original ItemGroup node.

  9. Close the csproj file, right click on the Project in Solution Explorer and select "Reload project".

  10. If the project was your startup project you may need to right click again and select "Set as StartUp Project"

  11. Attempt to build

like image 71
pwdst Avatar answered Sep 30 '22 08:09

pwdst