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 which is the same other that a different approach was taken and more added.
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.
Top comments (0)