DEV Community

Cover image for Side project idea II
Karen Payne
Karen Payne

Posted on

Side project idea II

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.

Source code

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"
      }
    ]
  }
Enter fullscreen mode Exit fullscreen mode

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
    };
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode
  • 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()
        );
Enter fullscreen mode Exit fullscreen mode

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)