Introduction
Learn how to get started with which the code presented can be done in.
Important
As written, its assumed the user has permission to read from the provided folder, if the user may not have permission to read from the folder wrapper the code in a try-catch.
Sample patterns
To match files in the file system based on user-defined patterns, start by instantiating a .
Models
The follow model will be used to capture solutions and list of project names.
/// <summary>
/// Represents a collection of solutions, each containing details about its name, folder, file name, and associated projects.
/// </summary>
internal class Solutions
{
/// <summary>
/// Get/set the name of the solution.
/// </summary>
/// <value>
/// The name of the solution.
/// </value>
public string Name { get; set; }
/// <summary>
/// Get/set the folder path where the solution is located.
/// </summary>
public string Folder { get; set; }
/// <summary>
/// Get/set the file name of the solution.
/// </summary>
/// <value>
/// A string representing the name of the solution file.
/// </value>
public string FileName { get; set; }
/// <summary>
/// Get/set the list of project file names associated with the solution.
/// </summary>
/// <value>
/// A list of project file names w/o path.
/// </value>
public List<string> Projects { get; set; } = [];
}
The following model is a container for each match found in _GlobSolutions _class, _ProcessSolutionFolderAsync _method.
/// <summary>
/// Represents a matched file item within a directory, including its folder and file name.
/// </summary>
public class FileMatchItem
{
public FileMatchItem(string sender)
{
Folder = Path.GetDirectoryName(sender);
FileName = Path.GetFileName(sender);
}
public string Folder { get; init; }
public string FileName { get; init; }
public override string ToString() => $"{Folder}\\{FileName}";
}
Working class
GetSolutionNames method accepts the path to a folder with one or more solutions which passes control to ProcessSolutionFolderAsync method.
ProcessSolutionFolderAsync method:
First parameter is the path to the folder containing one or more Visual Studio solutions.
Second parameter Action<FileMatchItem, string> represents the defined action to fire off, in this case GlobSolutions.ProcessFile which first checks if the solution name is in the list (in the same class), if not its added, otherwise project names are added.
Next control is handed over to GetProjectFiles method which is passed, the current solution path which uses globbing to get project names for the current solution.
internal class GlobSolutions
{
public static List<Solutions> Solutions = [];
/// <summary>
/// Asynchronously retrieves and processes the names of solution files in the specified directory.
/// </summary>
/// <param name="path">The directory path to search for solution files.</param>
public static async Task GetSolutionNames(string path)
{
await ProcessSolutionFolderAsync(path, ProcessFile);
}
/// <summary>
/// Processes a matched file and appends its details to the internal StringBuilder.
/// </summary>
/// <param name="fileMatch">The matched file item to process.</param>
/// <param name="solutionItem"></param>
private static void ProcessFile(FileMatchItem fileMatch, string solutionItem)
{
var solution = Solutions.FirstOrDefault(x => x.Name == solutionItem);
if (solution is not null)
{
solution.Projects.Add(fileMatch.FileName);
}
else
{
solution = new Solutions
{
Name = solutionItem,
FileName = Path.GetFileName(solutionItem),
Folder = Path.GetDirectoryName(solutionItem)
};
Solutions.Add(solution);
}
}
/// <summary>
/// Asynchronously processes solution files in the specified folder.
/// </summary>
/// <param name="folder">The folder to search for solution files.</param>
/// <param name="foundAction">The action to perform when a solution file is found.</param>
private static async Task ProcessSolutionFolderAsync(string folder, Action<FileMatchItem, string> foundAction)
{
Matcher matcher = new();
matcher.AddInclude("**/*.sln");
var files = matcher.GetResultsInFullPath(folder);
var tasks = files.Select(async file =>
{
foundAction?.Invoke(new FileMatchItem(file), file);
var list = await GetProjectFiles(Path.GetDirectoryName(file));
foreach (var item in list)
{
foundAction?.Invoke(item, file);
}
});
await Task.WhenAll(tasks);
}
/// <summary>
/// Asynchronously retrieves a list of project files in the specified parent folder.
/// </summary>
/// <param name="parentFolder">The parent folder to search for project files.</param>
/// <returns>The task result contains a list of <see cref="FileMatchItem"/> representing the project files found.</returns>
public static async Task<List<FileMatchItem>> GetProjectFiles(string parentFolder)
{
List<FileMatchItem> list = [];
Matcher matcher = new();
matcher.AddIncludePatterns(["**/*.csproj"]);
await Task.Run(() =>
{
foreach (var file in matcher.GetResultsInFullPath(parentFolder))
{
list.Add(new FileMatchItem(file));
}
});
return list;
}
}
ProcessSolutionFolderAsync performance
The first iteration of the code ran and found 152 solutions and over 1,300 projects which ran slow.
private static async Task ProcessSolutionFolderAsync(string folder, Action<FileMatchItem, string> foundAction)
{
Matcher matcher = new();
matcher.AddIncludePatterns(["**/*.sln"]);
await Task.Run(async () =>
{
foreach (var file in matcher.GetResultsInFullPath(folder))
{
foundAction?.Invoke(new FileMatchItem(file), file);
var list = await GetProjectFiles(Path.GetDirectoryName(file));
foreach (var item in list)
{
foundAction?.Invoke(item, file);
}
}
});
}
The reason appears to have been from invoking the action in the foreach.
GitHub Copilot to the rescue
Beings A.I. is so helpful, Copilot was invoked with /optimize and the results are shown below shaving off over 2 seconds of execution time. The author could had made the changes which might have taken ten minutes while Copilot did it in less than 20 seconds.
Start-up code
- path variable is the folder to scan for Visual Studio solutions
- Asserts if the folder exists
- Using NuGet package .↗ Original-Artikel auf dev.to lesenVollständiger Original-BerichtAusführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
SOCIAL SHARE CARD GENERATOR