Introduction
A great way for a developer to expand their knowledge is by creating side projects, which can turn into useful applications or code that can be used in another project.
When setting out on a side/learning project, consider writing down the objectives of what you intend to learn, rather than, for example, coding a game. Always document the code for future reference.
Make sure to check out Side project idea I.
Another reason for side projects is to showcase what you are capable of during interviews when applying for a new job.
Project objectives
Given a path to a Microsoft Visual Studio solution, read the target framework from each project group by framework version. Also presented is code that identifies projects that target multiple frameworks in a console project.
Main method
For some developers, the main method might seem a mess, but once time is spent with the code, the code will make sense, as this is how a learning project may appear, while the meat of the code is well thought out and fully documented.
Example json
{
"Framework": "net8.0",
"Projects": [
{
"ProjectName": "BogusDateOnlyTimeOnlyApp",
"ProjectPath": "C:\\DotnetLand\\VS2022\\HowToSeriesSolution\\BogusDateOnlyTimeOnlyApp\\BogusDateOnlyTimeOnlyApp.csproj"
},
{
"ProjectName": "BogusLibrary1",
"ProjectPath": "C:\\DotnetLand\\VS2022\\HowToSeriesSolution\\BogusLibrary1\\BogusLibrary1.csproj"
}
]
}
Learning points
- Using models (classes) rather than anonymous types
- Entire project organization
- Traversing directories for .csproj, .vbproj and .fsproj files to retrieve frameworks
public static List<ProjectFrameworkInfo> ReadSolution(string solutionPath)
{
if (!File.Exists(solutionPath))
throw new FileNotFoundException("Solution not found.", solutionPath);
var slnDir = Path.GetDirectoryName(Path.GetFullPath(solutionPath))!;
var projectPaths = GetProjectPathsFromSln(solutionPath, slnDir);
var results = new List<ProjectFrameworkInfo>();
foreach (var projPath in projectPaths)
{
var info = ReadProjectFrameworks(projPath);
results.Add(info);
}
return results;
}
private static List<string> GetProjectPathsFromSln(string slnPath, string slnDir)
{
// Very simple .sln parsing: finds lines like:
// Project("{GUID}") = "Name", "path\to\proj.csproj", "{GUID}"
var paths = new List<string>();
foreach (var line in File.ReadLines(slnPath))
{
if (!line.StartsWith("Project(", StringComparison.OrdinalIgnoreCase))
continue;
var parts = line.Split(',');
if (parts.Length < 2)
continue;
var rel = parts[1].Trim().Trim('"');
// only MSBuild-style projects
if (!rel.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase) &&
!rel.EndsWith(".vbproj", StringComparison.OrdinalIgnoreCase) &&
!rel.EndsWith(".fsproj", StringComparison.OrdinalIgnoreCase))
continue;
var full = Path.GetFullPath(Path.Combine(slnDir, rel));
if (File.Exists(full))
paths.Add(full);
}
return paths.Distinct(StringComparer.OrdinalIgnoreCase).ToList();
}
private static ProjectFrameworkInfo ReadProjectFrameworks(string projectPath)
{
var doc = XDocument.Load(projectPath, LoadOptions.PreserveWhitespace);
// SDK-style csproj usually has no XML namespace; handle both anyway.
var root = doc.Root ?? throw new InvalidOperationException($"Invalid project XML: {projectPath}");
XNamespace ns = root.Name.Namespace;
// Prefer explicit TargetFramework(s) anywhere in PropertyGroup
var tf = root.Descendants(ns + "TargetFramework").Select(e => (string?)e).FirstOrDefault(v => !string.IsNullOrWhiteSpace(v))?.Trim();
var tfsRaw = root.Descendants(ns + "TargetFrameworks").Select(e => (string?)e).FirstOrDefault(v => !string.IsNullOrWhiteSpace(v))?.Trim();
var tfs = SplitFrameworks(tfsRaw);
return new ProjectFrameworkInfo
{
ProjectName = Path.GetFileNameWithoutExtension(projectPath),
ProjectPath = projectPath,
TargetFramework = tf,
TargetFrameworks = tfs
};
}
New to some developers
- Grouping data
- Use of SelectMany projects each element of a sequence to an IEnumerable<T> and flattens the resulting sequences into one sequence.
List<GroupedItems> groupedByFramework =
projects
.SelectMany(p => p.AllFrameworks.Select(f => new GroupedProject(f, p)))
.GroupBy(x => x.Framework)
.OrderBy(g => g.Key)
.Select(g => new GroupedItems(g.Key, g
.OrderBy(x => x.Project.ProjectName)
.Select(x => new GroupedItem(x.Project.ProjectName, x.Project.ProjectPath))
.ToList()))
.ToList();
- ToDictionary
Dictionary<string, List<string>> groupedDictionary =
projects
.SelectMany(p => p.AllFrameworks.Select(f => new GroupedProject(f, p)))
.GroupBy(x => x.Framework)
.ToDictionary(
g => g.Key,
g => g
.OrderBy(x => x.Project.ProjectName)
.Select(x => x.Project.ProjectName)
.ToList()
);
Use of AI tools
When using AI, make sure to fully understand its responses, as the purpose of learning projects is to learn, not vibe-code.
Summary
Both side project one and two are for learning, while the next in the series will focus on a project which provides an idea on designing a database using AI and how to code against the database using EF Core, while the frontend will be left to the reader, as this provides the reader a choice of frontend project from desktop, mobile, or web-based.
SOCIAL SHARE CARD GENERATOR