Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Build

on:
push:
branches:
- main
- "feature/**"
pull_request:
branches:
- main
workflow_dispatch:

permissions:
contents: read

env:
SOLUTION_PATH: src/CSharpFundamentals/CSharpFundamentals.slnx

jobs:
build:
name: Build (.NET 10)
runs-on: ubuntu-latest

steps:
- name: Check out repository
uses: actions/checkout@v6

- name: Set up .NET 10
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x

- name: Restore dependencies
run: dotnet restore "$SOLUTION_PATH"

- name: Build in Release mode
run: dotnet build "$SOLUTION_PATH" --configuration Release --no-restore --warnaserror

- name: Verify executable examples
run: >-
dotnet run
--project src/CSharpFundamentals/CSharpFundamentals/CSharpFundamentals.csproj
--configuration Release
--no-build
--
verify
39 changes: 39 additions & 0 deletions .github/workflows/markdown-links.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Markdown Links

on:
push:
branches:
- main
paths:
- "**/*.md"
- ".github/workflows/markdown-links.yml"
pull_request:
branches:
- main
paths:
- "**/*.md"
- ".github/workflows/markdown-links.yml"
workflow_dispatch:

permissions:
contents: read

jobs:
links:
name: Validate Markdown links
runs-on: ubuntu-latest

steps:
- name: Check out repository
uses: actions/checkout@v6

- name: Check links
uses: lycheeverse/lychee-action@v2.8.0
with:
args: >-
--root-dir "${{ github.workspace }}"
--verbose
--no-progress
"./**/*.md"
fail: true
jobSummary: true
59 changes: 35 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<div align="center">
<img src="./assets/csharp-fundamentals-banner.svg" alt="C# engineering reference" width="100%" />

[![Build](https://github.com/Sahithbasani/csharp-fundamentals/actions/workflows/build.yml/badge.svg)](https://github.com/Sahithbasani/csharp-fundamentals/actions/workflows/build.yml)
[![Markdown Links](https://github.com/Sahithbasani/csharp-fundamentals/actions/workflows/markdown-links.yml/badge.svg)](https://github.com/Sahithbasani/csharp-fundamentals/actions/workflows/markdown-links.yml)
[![C#](https://img.shields.io/badge/C%23-engineering-512BD4?logo=csharp&logoColor=white)](https://learn.microsoft.com/dotnet/csharp/)
[![.NET](https://img.shields.io/badge/.NET-10-512BD4?logo=dotnet&logoColor=white)](https://dotnet.microsoft.com/)
[![Modules](https://img.shields.io/badge/modules-10-0F766E)](#module-index)
[![License](https://img.shields.io/badge/license-MIT-2563EB)](./LICENSE)
[![License](https://img.shields.io/github/license/Sahithbasani/csharp-fundamentals?color=2563EB)](./LICENSE)
</div>

# C# Engineering Fundamentals
Expand All @@ -16,10 +18,12 @@ This repository is not organized as a syntax tutorial. Each module is an enginee
## Table of Contents

- [Engineering Model](#engineering-model)
- [Engineering Quality](#engineering-quality)
- [Module Index](#module-index)
- [Documentation Standard](#documentation-standard)
- [Runnable Scenarios](#runnable-scenarios)
- [Build and Validate](#build-and-validate)
- [Technical Blog Watch](#technical-blog-watch)
- [Repository Structure](#repository-structure)
- [Contribution Standard](#contribution-standard)

Expand All @@ -34,6 +38,16 @@ flowchart LR
Operations --> Review["Review and interview reasoning"]
```

## Engineering Quality

Repository quality is enforced through small, reviewable changes and automated verification:

- **Continuous integration:** every push to `main` or a feature branch and every pull request to `main` restores, builds, and verifies the executable examples in Release mode.
- **Strict compilation:** CI treats compiler warnings as errors to prevent warning debt from entering the stable branch.
- **Behavior verification:** `dotnet run -- verify` exercises the example decision paths and fails fast when expected outcomes drift.
- **Documentation integrity:** Markdown links are checked automatically when documentation changes.
- **Reproducible tooling:** workflows install the .NET 10 SDK explicitly instead of relying on the runner's preinstalled SDKs.

## Module Index

| Module | Engineering area | Code examples |
Expand All @@ -53,21 +67,22 @@ flowchart LR

Every module contains:

- `README.md` decision-oriented overview and navigation;
- `Theory.md` language and runtime semantics;
- `BestPractices.md` implementation and review checklist;
- `CommonMistakes.md` production failure patterns;
- `InterviewQuestions.md` 10 senior-level questions with answers;
- `ProductionNotes.md` enterprise use, memory, performance, and Microsoft-aligned recommendations;
- compileable `.cs` examples using orders, payments, users, claims, services, and infrastructure boundaries.
- `README.md` - decision-oriented overview and navigation
- `Theory.md` - language and runtime semantics
- `BestPractices.md` - implementation and review checklist
- `CommonMistakes.md` - production failure patterns
- `InterviewQuestions.md` - 10 senior-level questions with answers
- `ProductionNotes.md` - enterprise use, memory, performance, and Microsoft-aligned recommendations
- compileable `.cs` examples using orders, payments, users, claims, services, and infrastructure boundaries

## Runnable Scenarios

The application includes executable order processing, payment validation, and notification dispatch examples.
The application includes executable order processing, payment validation, and notification dispatch examples, plus a built-in verification command for deterministic behavior checks.

```bash
dotnet run --project src/CSharpFundamentals/CSharpFundamentals/CSharpFundamentals.csproj
dotnet run --project src/CSharpFundamentals/CSharpFundamentals/CSharpFundamentals.csproj -- payment
dotnet run --project src/CSharpFundamentals/CSharpFundamentals/CSharpFundamentals.csproj -- verify
```

See the [execution guide](./examples/README.md).
Expand All @@ -79,27 +94,23 @@ Requires the [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0).
```bash
dotnet restore src/CSharpFundamentals/CSharpFundamentals.slnx
dotnet build src/CSharpFundamentals/CSharpFundamentals.slnx --configuration Release
dotnet run --project src/CSharpFundamentals/CSharpFundamentals/CSharpFundamentals.csproj --configuration Release --no-build -- verify
```

## Technical Blog Watch

- [.NET Team: Announcing .NET 10](https://devblogs.microsoft.com/dotnet/announcing-dotnet-10/) explains the runtime, language, and tooling release this repository targets.

## Repository Structure

```text
csharp-fundamentals/
├── assets/
├── examples/
└── src/CSharpFundamentals/CSharpFundamentals/
├── Examples/ # Executable cross-topic scenarios
└── Modules/
├── 01-Variables/
├── 02-DataTypes/
├── 03-TypeConversion/
├── 04-Operators/
├── 05-Strings/
├── 06-Arrays/
├── 07-Methods/
├── 08-ControlFlow/
├── 09-ExceptionHandling/
└── 10-Namespaces/
|-- assets/
|-- examples/
`-- src/CSharpFundamentals/CSharpFundamentals/
|-- Examples/ # Executable cross-topic scenarios
|-- Modules/
`-- Verification/ # Built-in behavior checks for the examples
```

## Contribution Standard
Expand Down
28 changes: 18 additions & 10 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Runnable Backend Examples

[Repository home](../README.md) · [Module index](../README.md#module-index)
[Repository home](../README.md) | [Module index](../README.md#module-index)

These examples are small enough to run locally while using domains and design concerns found in backend services.

Expand All @@ -10,6 +10,14 @@ These examples are small enough to run locally while using domains and design co
dotnet run --project src/CSharpFundamentals/CSharpFundamentals/CSharpFundamentals.csproj
```

## Verify Expected Behavior

```bash
dotnet run --project src/CSharpFundamentals/CSharpFundamentals/CSharpFundamentals.csproj -- verify
```

The `verify` command checks the reusable order, payment, and notification decision paths so regressions surface before a pull request is opened.

## Available Examples

| Argument | Scenario | Concepts |
Expand All @@ -26,9 +34,9 @@ dotnet run --project src/CSharpFundamentals/CSharpFundamentals/CSharpFundamental

Expected behavior:

- validates customer and line data;
- calculates subtotal, tax, and total;
- flags orders at or above the review threshold.
- validates customer and line data
- calculates subtotal, tax, and total
- flags orders at or above the review threshold

### Payment Validation

Expand All @@ -38,9 +46,9 @@ dotnet run --project src/CSharpFundamentals/CSharpFundamentals/CSharpFundamental

Expected behavior:

- rejects invalid amounts or unsupported currencies;
- routes high-risk or large payments to manual review;
- returns a decision object rather than using exceptions for expected outcomes.
- rejects invalid amounts or unsupported currencies
- routes high-risk or large payments to manual review
- returns a decision object rather than using exceptions for expected outcomes

### Notification Dispatch

Expand All @@ -50,9 +58,9 @@ dotnet run --project src/CSharpFundamentals/CSharpFundamentals/CSharpFundamental

Expected behavior:

- resolves a user through a repository interface;
- respects notification preferences;
- sends through an asynchronous gateway with cancellation support.
- resolves a user through a repository interface
- respects notification preferences
- sends through an asynchronous gateway with cancellation support

## Practice Extensions

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,65 +21,4 @@ public async Task RunAsync(CancellationToken cancellationToken)

Console.WriteLine($"Notification status: {result.Status}");
}

private interface IUserRepository
{
Task<User?> FindAsync(Guid userId, CancellationToken cancellationToken);
}

private interface IMessageGateway
{
Task SendAsync(string destination, string message, CancellationToken cancellationToken);
}

private sealed class NotificationService(
IUserRepository users,
IMessageGateway gateway)
{
public async Task<NotificationResult> NotifyAsync(
Guid userId,
string message,
CancellationToken cancellationToken)
{
User? user = await users.FindAsync(userId, cancellationToken);

if (user is null)
return new NotificationResult("User not found");

if (!user.NotificationsEnabled)
return new NotificationResult("Notifications disabled");

await gateway.SendAsync(user.Email, message, cancellationToken);
return new NotificationResult("Sent");
}
}

private sealed class InMemoryUserRepository(IEnumerable<User> users) : IUserRepository
{
private readonly IReadOnlyDictionary<Guid, User> _users =
users.ToDictionary(user => user.Id);

public Task<User?> FindAsync(Guid userId, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
_users.TryGetValue(userId, out User? user);
return Task.FromResult(user);
}
}

private sealed class ConsoleMessageGateway : IMessageGateway
{
public async Task SendAsync(
string destination,
string message,
CancellationToken cancellationToken)
{
await Task.Delay(25, cancellationToken);
Console.WriteLine($"Sending to {destination}: {message}");
}
}

private sealed record User(Guid Id, string Email, bool NotificationsEnabled);

private sealed record NotificationResult(string Status);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
namespace CSharpFundamentals.Examples;

internal interface IUserRepository
{
Task<User?> FindAsync(Guid userId, CancellationToken cancellationToken);
}

internal interface IMessageGateway
{
Task SendAsync(string destination, string message, CancellationToken cancellationToken);
}

internal sealed class NotificationService(
IUserRepository users,
IMessageGateway gateway)
{
public async Task<NotificationResult> NotifyAsync(
Guid userId,
string message,
CancellationToken cancellationToken)
{
User? user = await users.FindAsync(userId, cancellationToken);

if (user is null)
return new NotificationResult("User not found");

if (!user.NotificationsEnabled)
return new NotificationResult("Notifications disabled");

await gateway.SendAsync(user.Email, message, cancellationToken);
return new NotificationResult("Sent");
}
}

internal sealed class InMemoryUserRepository(IEnumerable<User> users) : IUserRepository
{
private readonly IReadOnlyDictionary<Guid, User> _users =
users.ToDictionary(user => user.Id);

public Task<User?> FindAsync(Guid userId, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
_users.TryGetValue(userId, out User? user);
return Task.FromResult(user);
}
}

internal sealed class ConsoleMessageGateway : IMessageGateway
{
public async Task SendAsync(
string destination,
string message,
CancellationToken cancellationToken)
{
await Task.Delay(25, cancellationToken);
Console.WriteLine($"Sending to {destination}: {message}");
}
}

internal sealed record User(Guid Id, string Email, bool NotificationsEnabled);

internal sealed record NotificationResult(string Status);
Loading