@softspark/ai-toolkit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +412 -0
- package/CHANGELOG.md +68 -0
- package/LICENSE +21 -0
- package/README.md +632 -0
- package/action.yml +53 -0
- package/app/.claude-plugin/plugin.json +44 -0
- package/app/ARCHITECTURE.md +306 -0
- package/app/CLAUDE.md.template +23 -0
- package/app/agents/ai-engineer.md +128 -0
- package/app/agents/backend-specialist.md +193 -0
- package/app/agents/business-intelligence.md +54 -0
- package/app/agents/chaos-monkey.md +67 -0
- package/app/agents/chief-of-staff.md +51 -0
- package/app/agents/code-archaeologist.md +127 -0
- package/app/agents/code-reviewer.md +184 -0
- package/app/agents/command-expert.md +131 -0
- package/app/agents/data-analyst.md +205 -0
- package/app/agents/data-scientist.md +151 -0
- package/app/agents/database-architect.md +317 -0
- package/app/agents/debugger.md +238 -0
- package/app/agents/devops-implementer.md +194 -0
- package/app/agents/documenter.md +364 -0
- package/app/agents/explorer-agent.md +145 -0
- package/app/agents/fact-checker.md +172 -0
- package/app/agents/frontend-specialist.md +209 -0
- package/app/agents/game-developer.md +216 -0
- package/app/agents/incident-responder.md +226 -0
- package/app/agents/infrastructure-architect.md +127 -0
- package/app/agents/infrastructure-validator.md +247 -0
- package/app/agents/llm-ops-engineer.md +237 -0
- package/app/agents/mcp-expert.md +228 -0
- package/app/agents/mcp-server-architect.md +195 -0
- package/app/agents/mcp-testing-engineer.md +292 -0
- package/app/agents/meta-architect.md +58 -0
- package/app/agents/ml-engineer.md +136 -0
- package/app/agents/mobile-developer.md +190 -0
- package/app/agents/night-watchman.md +55 -0
- package/app/agents/nlp-engineer.md +154 -0
- package/app/agents/orchestrator.md +437 -0
- package/app/agents/performance-optimizer.md +254 -0
- package/app/agents/predictive-analyst.md +57 -0
- package/app/agents/product-manager.md +194 -0
- package/app/agents/project-planner.md +287 -0
- package/app/agents/prompt-engineer.md +103 -0
- package/app/agents/qa-automation-engineer.md +182 -0
- package/app/agents/rag-engineer.md +201 -0
- package/app/agents/research-synthesizer.md +138 -0
- package/app/agents/search-specialist.md +101 -0
- package/app/agents/security-architect.md +62 -0
- package/app/agents/security-auditor.md +293 -0
- package/app/agents/seo-specialist.md +111 -0
- package/app/agents/system-governor.md +57 -0
- package/app/agents/tech-lead.md +62 -0
- package/app/agents/technical-researcher.md +103 -0
- package/app/agents/test-engineer.md +264 -0
- package/app/constitution.md +38 -0
- package/app/hooks/_profile-check.sh +11 -0
- package/app/hooks/guard-destructive.sh +74 -0
- package/app/hooks/guard-path.sh +73 -0
- package/app/hooks/post-tool-use.sh +35 -0
- package/app/hooks/pre-compact.sh +31 -0
- package/app/hooks/quality-check.sh +22 -0
- package/app/hooks/quality-gate.sh +49 -0
- package/app/hooks/save-session.sh +24 -0
- package/app/hooks/session-end.sh +37 -0
- package/app/hooks/session-start.sh +29 -0
- package/app/hooks/subagent-start.sh +16 -0
- package/app/hooks/subagent-stop.sh +16 -0
- package/app/hooks/track-usage.sh +50 -0
- package/app/hooks/user-prompt-submit.sh +25 -0
- package/app/hooks.json +178 -0
- package/app/mcp-defaults.json +23 -0
- package/app/output-styles/golden-rules.md +43 -0
- package/app/plugins/README.md +19 -0
- package/app/plugins/csharp-pack/README.md +11 -0
- package/app/plugins/csharp-pack/plugin.json +18 -0
- package/app/plugins/enterprise-pack/README.md +16 -0
- package/app/plugins/enterprise-pack/hooks/output-style.sh +6 -0
- package/app/plugins/enterprise-pack/hooks/status-line.sh +8 -0
- package/app/plugins/enterprise-pack/plugin.json +24 -0
- package/app/plugins/frontend-pack/README.md +14 -0
- package/app/plugins/frontend-pack/plugin.json +22 -0
- package/app/plugins/java-pack/README.md +11 -0
- package/app/plugins/java-pack/plugin.json +18 -0
- package/app/plugins/kotlin-pack/README.md +11 -0
- package/app/plugins/kotlin-pack/plugin.json +18 -0
- package/app/plugins/memory-pack/README.md +24 -0
- package/app/plugins/memory-pack/hooks/observation-capture.sh +67 -0
- package/app/plugins/memory-pack/hooks/session-summary.sh +71 -0
- package/app/plugins/memory-pack/plugin.json +22 -0
- package/app/plugins/memory-pack/scripts/init_db.py +81 -0
- package/app/plugins/memory-pack/scripts/strip_private.py +22 -0
- package/app/plugins/memory-pack/skills/mem-search/SKILL.md +70 -0
- package/app/plugins/research-pack/README.md +14 -0
- package/app/plugins/research-pack/plugin.json +22 -0
- package/app/plugins/ruby-pack/README.md +11 -0
- package/app/plugins/ruby-pack/plugin.json +18 -0
- package/app/plugins/rust-pack/README.md +11 -0
- package/app/plugins/rust-pack/plugin.json +18 -0
- package/app/plugins/security-pack/README.md +15 -0
- package/app/plugins/security-pack/plugin.json +23 -0
- package/app/plugins/swift-pack/README.md +11 -0
- package/app/plugins/swift-pack/plugin.json +18 -0
- package/app/rules/claude-toolkit-rules.md +21 -0
- package/app/rules/git-conventions.md +5 -0
- package/app/rules/quality-gates.md +10 -0
- package/app/skills/_lib/__init__.py +1 -0
- package/app/skills/_lib/detect_utils.py +150 -0
- package/app/skills/agent-creator/SKILL.md +82 -0
- package/app/skills/analyze/SKILL.md +92 -0
- package/app/skills/analyze/scripts/complexity.py +165 -0
- package/app/skills/api-patterns/SKILL.md +305 -0
- package/app/skills/app-builder/SKILL.md +187 -0
- package/app/skills/architecture-audit/SKILL.md +141 -0
- package/app/skills/architecture-decision/SKILL.md +55 -0
- package/app/skills/architecture-decision/templates/adr-template.md +36 -0
- package/app/skills/biz-scan/SKILL.md +30 -0
- package/app/skills/briefing/SKILL.md +27 -0
- package/app/skills/build/SKILL.md +97 -0
- package/app/skills/build/scripts/detect-build.py +151 -0
- package/app/skills/chaos/SKILL.md +32 -0
- package/app/skills/ci/SKILL.md +77 -0
- package/app/skills/ci/scripts/ci-detect.py +135 -0
- package/app/skills/ci/templates/github-actions-node.yml +38 -0
- package/app/skills/ci/templates/github-actions-python.yml +42 -0
- package/app/skills/ci-cd-patterns/SKILL.md +299 -0
- package/app/skills/clean-code/SKILL.md +110 -0
- package/app/skills/clean-code/reference/dart.md +18 -0
- package/app/skills/clean-code/reference/go.md +23 -0
- package/app/skills/clean-code/reference/php.md +32 -0
- package/app/skills/clean-code/reference/python.md +180 -0
- package/app/skills/clean-code/reference/typescript.md +26 -0
- package/app/skills/command-creator/SKILL.md +83 -0
- package/app/skills/commit/SKILL.md +98 -0
- package/app/skills/commit/scripts/pre-commit-check.py +87 -0
- package/app/skills/commit/templates/conventional-commit.md +52 -0
- package/app/skills/csharp-patterns/SKILL.md +450 -0
- package/app/skills/database-patterns/SKILL.md +297 -0
- package/app/skills/debug/SKILL.md +154 -0
- package/app/skills/debug/scripts/error-parser.py +187 -0
- package/app/skills/debugging-tactics/SKILL.md +136 -0
- package/app/skills/deploy/SKILL.md +130 -0
- package/app/skills/deploy/scripts/pre_deploy_check.py +171 -0
- package/app/skills/deploy/templates/deployment-checklist.md +31 -0
- package/app/skills/design-an-interface/SKILL.md +105 -0
- package/app/skills/design-engineering/SKILL.md +260 -0
- package/app/skills/docker-devops/SKILL.md +303 -0
- package/app/skills/docs/SKILL.md +145 -0
- package/app/skills/docs/scripts/doc-inventory.py +176 -0
- package/app/skills/docs/templates/adr-template.md +36 -0
- package/app/skills/docs/templates/readme-template.md +67 -0
- package/app/skills/documentation-standards/SKILL.md +191 -0
- package/app/skills/ecommerce-patterns/SKILL.md +209 -0
- package/app/skills/evaluate/SKILL.md +132 -0
- package/app/skills/evolve/SKILL.md +27 -0
- package/app/skills/explain/SKILL.md +54 -0
- package/app/skills/explain/scripts/dependency-graph.py +215 -0
- package/app/skills/explore/SKILL.md +112 -0
- package/app/skills/explore/scripts/visualize.py +117 -0
- package/app/skills/fix/SKILL.md +78 -0
- package/app/skills/fix/scripts/error-classifier.py +191 -0
- package/app/skills/flutter-patterns/SKILL.md +254 -0
- package/app/skills/git-mastery/SKILL.md +70 -0
- package/app/skills/grill-me/SKILL.md +38 -0
- package/app/skills/health/SKILL.md +91 -0
- package/app/skills/health/scripts/health_check.py +162 -0
- package/app/skills/hive-mind/SKILL.md +56 -0
- package/app/skills/hook-creator/SKILL.md +107 -0
- package/app/skills/index/SKILL.md +74 -0
- package/app/skills/instinct-review/SKILL.md +77 -0
- package/app/skills/java-patterns/SKILL.md +442 -0
- package/app/skills/kotlin-patterns/SKILL.md +446 -0
- package/app/skills/lint/SKILL.md +103 -0
- package/app/skills/lint/scripts/detect-linters.py +112 -0
- package/app/skills/mcp-patterns/SKILL.md +270 -0
- package/app/skills/mem-search/SKILL.md +70 -0
- package/app/skills/migrate/SKILL.md +90 -0
- package/app/skills/migrate/scripts/migration-status.py +195 -0
- package/app/skills/migration-patterns/SKILL.md +260 -0
- package/app/skills/night-watch/SKILL.md +28 -0
- package/app/skills/observability-patterns/SKILL.md +203 -0
- package/app/skills/onboard/SKILL.md +76 -0
- package/app/skills/orchestrate/SKILL.md +86 -0
- package/app/skills/panic/SKILL.md +30 -0
- package/app/skills/performance-profiling/SKILL.md +59 -0
- package/app/skills/plan/SKILL.md +110 -0
- package/app/skills/plan/templates/plan-template.md +40 -0
- package/app/skills/plan-writing/SKILL.md +201 -0
- package/app/skills/plugin-creator/SKILL.md +78 -0
- package/app/skills/pr/SKILL.md +129 -0
- package/app/skills/pr/scripts/pr-summary.py +175 -0
- package/app/skills/prd-to-issues/SKILL.md +108 -0
- package/app/skills/prd-to-plan/SKILL.md +120 -0
- package/app/skills/predict/SKILL.md +30 -0
- package/app/skills/qa-session/SKILL.md +110 -0
- package/app/skills/rag-patterns/SKILL.md +203 -0
- package/app/skills/refactor/SKILL.md +124 -0
- package/app/skills/refactor/scripts/refactor-scan.py +210 -0
- package/app/skills/refactor-plan/SKILL.md +112 -0
- package/app/skills/repeat/SKILL.md +149 -0
- package/app/skills/research-mastery/SKILL.md +56 -0
- package/app/skills/review/SKILL.md +141 -0
- package/app/skills/review/scripts/diff-analyzer.py +170 -0
- package/app/skills/rollback/SKILL.md +87 -0
- package/app/skills/rollback/scripts/rollback_info.py +149 -0
- package/app/skills/ruby-patterns/SKILL.md +454 -0
- package/app/skills/rust-patterns/SKILL.md +446 -0
- package/app/skills/search/SKILL.md +64 -0
- package/app/skills/security-patterns/SKILL.md +91 -0
- package/app/skills/security-patterns/reference/authentication.md +37 -0
- package/app/skills/security-patterns/reference/authorization.md +22 -0
- package/app/skills/security-patterns/reference/input-validation.md +30 -0
- package/app/skills/security-patterns/reference/oauth-csrf-audit.md +131 -0
- package/app/skills/skill-creator/SKILL.md +154 -0
- package/app/skills/skill-creator/templates/dashboard/index.html +130 -0
- package/app/skills/skill-creator/templates/reasoning-engine/assets/example.json +12 -0
- package/app/skills/skill-creator/templates/reasoning-engine/search.py +110 -0
- package/app/skills/subagent-development/SKILL.md +225 -0
- package/app/skills/subagent-development/reference/code-quality-reviewer-prompt.md +145 -0
- package/app/skills/subagent-development/reference/implementer-prompt.md +118 -0
- package/app/skills/subagent-development/reference/spec-reviewer-prompt.md +100 -0
- package/app/skills/swarm/SKILL.md +81 -0
- package/app/skills/swift-patterns/SKILL.md +500 -0
- package/app/skills/tdd/SKILL.md +174 -0
- package/app/skills/tdd/reference/deep-modules.md +32 -0
- package/app/skills/tdd/reference/interface-design.md +32 -0
- package/app/skills/tdd/reference/mocking.md +52 -0
- package/app/skills/tdd/reference/refactoring.md +10 -0
- package/app/skills/tdd/reference/tests.md +59 -0
- package/app/skills/teams/SKILL.md +101 -0
- package/app/skills/test/SKILL.md +107 -0
- package/app/skills/test/scripts/detect-runner.py +113 -0
- package/app/skills/testing-patterns/SKILL.md +73 -0
- package/app/skills/testing-patterns/reference/flutter-testing.md +33 -0
- package/app/skills/testing-patterns/reference/go-testing.md +52 -0
- package/app/skills/testing-patterns/reference/php-phpunit.md +39 -0
- package/app/skills/testing-patterns/reference/python-pytest.md +228 -0
- package/app/skills/testing-patterns/reference/typescript-vitest.md +50 -0
- package/app/skills/triage-issue/SKILL.md +120 -0
- package/app/skills/typescript-patterns/SKILL.md +256 -0
- package/app/skills/ubiquitous-language/SKILL.md +74 -0
- package/app/skills/verification-before-completion/SKILL.md +108 -0
- package/app/skills/workflow/SKILL.md +250 -0
- package/app/skills/write-a-prd/SKILL.md +129 -0
- package/app/skills/write-a-prd/reference/visual-companion.md +78 -0
- package/app/skills/write-a-prd/scripts/frame-template.html +111 -0
- package/app/skills/write-a-prd/scripts/visual-server.cjs +79 -0
- package/app/templates/skill/generator/SKILL.md.template +40 -0
- package/app/templates/skill/knowledge/SKILL.md.template +52 -0
- package/app/templates/skill/linter/SKILL.md.template +34 -0
- package/app/templates/skill/reviewer/SKILL.md.template +51 -0
- package/app/templates/skill/workflow/SKILL.md.template +49 -0
- package/benchmarks/README.md +111 -0
- package/benchmarks/ecosystem-dashboard.json +148 -0
- package/benchmarks/ecosystem-harvest.json +148 -0
- package/benchmarks/results.json +38 -0
- package/benchmarks/run.py +351 -0
- package/bin/ai-toolkit.js +345 -0
- package/kb/best-practices/README.md +11 -0
- package/kb/howto/README.md +11 -0
- package/kb/procedures/maintenance-sop.md +306 -0
- package/kb/reference/agents-catalog.md +124 -0
- package/kb/reference/anti-pattern-registry-format.md +221 -0
- package/kb/reference/architecture-overview.md +232 -0
- package/kb/reference/benchmark-config.md +62 -0
- package/kb/reference/ci-integration.md +66 -0
- package/kb/reference/claude-ecosystem-benchmark-snapshot.md +80 -0
- package/kb/reference/claude-ecosystem-expansion-foundations.md +102 -0
- package/kb/reference/commands-catalog.md +21 -0
- package/kb/reference/distribution-model.md +63 -0
- package/kb/reference/global-install-model.md +56 -0
- package/kb/reference/hierarchical-override-pattern.md +200 -0
- package/kb/reference/hooks-catalog.md +306 -0
- package/kb/reference/integrations.md +88 -0
- package/kb/reference/language-packs.md +52 -0
- package/kb/reference/merge-friendly-install-model.md +58 -0
- package/kb/reference/plugin-pack-conventions.md +151 -0
- package/kb/reference/quick-wins-implementation-summary.md +70 -0
- package/kb/reference/skill-templates.md +50 -0
- package/kb/reference/skills-catalog.md +215 -0
- package/kb/reference/skills-unification.md +57 -0
- package/kb/reference/stats.md +69 -0
- package/kb/reference/sync.md +76 -0
- package/kb/troubleshooting/README.md +11 -0
- package/llms-full.txt +3068 -0
- package/llms.txt +39 -0
- package/package.json +75 -0
- package/scripts/_common.py +160 -0
- package/scripts/add_rule.py +50 -0
- package/scripts/benchmark_config.py +127 -0
- package/scripts/benchmark_ecosystem.py +288 -0
- package/scripts/check_deps.py +260 -0
- package/scripts/create_skill.py +118 -0
- package/scripts/doctor.py +504 -0
- package/scripts/eject.py +113 -0
- package/scripts/emission.py +256 -0
- package/scripts/evaluate_skills.py +260 -0
- package/scripts/frontmatter.py +58 -0
- package/scripts/generate_agents_md.py +91 -0
- package/scripts/generate_aider_conf.py +51 -0
- package/scripts/generate_cline.py +35 -0
- package/scripts/generate_copilot.py +30 -0
- package/scripts/generate_cursor_rules.py +35 -0
- package/scripts/generate_gemini.py +28 -0
- package/scripts/generate_llms_txt.py +164 -0
- package/scripts/generate_roo_modes.py +80 -0
- package/scripts/generate_windsurf.py +35 -0
- package/scripts/generator_base.py +140 -0
- package/scripts/harvest_ecosystem.py +50 -0
- package/scripts/inject_rule_cli.py +101 -0
- package/scripts/inject_section_cli.py +47 -0
- package/scripts/injection.py +180 -0
- package/scripts/install.py +236 -0
- package/scripts/install_git_hooks.py +71 -0
- package/scripts/install_steps/__init__.py +5 -0
- package/scripts/install_steps/ai_tools.py +261 -0
- package/scripts/install_steps/hooks.py +90 -0
- package/scripts/install_steps/markers.py +79 -0
- package/scripts/install_steps/symlinks.py +87 -0
- package/scripts/merge-hooks.py +192 -0
- package/scripts/plugin.py +642 -0
- package/scripts/plugin_schema.py +138 -0
- package/scripts/remove_rule.py +58 -0
- package/scripts/stats.py +81 -0
- package/scripts/sync.py +215 -0
- package/scripts/uninstall.py +292 -0
- package/scripts/validate.py +700 -0
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: csharp-patterns
|
|
3
|
+
description: "Loaded when user asks about C# or .NET development patterns"
|
|
4
|
+
effort: medium
|
|
5
|
+
user-invocable: false
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# C# / .NET Patterns
|
|
9
|
+
|
|
10
|
+
## Project Structure
|
|
11
|
+
|
|
12
|
+
### Solution Layout
|
|
13
|
+
```
|
|
14
|
+
MyApp.sln
|
|
15
|
+
Directory.Build.props # Shared build properties
|
|
16
|
+
Directory.Packages.props # Central package management
|
|
17
|
+
src/
|
|
18
|
+
MyApp.Api/ # ASP.NET Core host (Controllers, Middleware, Program.cs)
|
|
19
|
+
MyApp.Application/ # Use cases, MediatR handlers, Behaviors
|
|
20
|
+
MyApp.Domain/ # Entities, value objects, domain events
|
|
21
|
+
MyApp.Infrastructure/ # EF Core, external services
|
|
22
|
+
tests/
|
|
23
|
+
MyApp.UnitTests/
|
|
24
|
+
MyApp.IntegrationTests/
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Directory.Build.props
|
|
28
|
+
```xml
|
|
29
|
+
<Project>
|
|
30
|
+
<PropertyGroup>
|
|
31
|
+
<TargetFramework>net9.0</TargetFramework>
|
|
32
|
+
<Nullable>enable</Nullable>
|
|
33
|
+
<ImplicitUsings>enable</ImplicitUsings>
|
|
34
|
+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
|
35
|
+
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
|
36
|
+
<AnalysisLevel>latest-recommended</AnalysisLevel>
|
|
37
|
+
</PropertyGroup>
|
|
38
|
+
</Project>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Central Package Management (Directory.Packages.props)
|
|
42
|
+
```xml
|
|
43
|
+
<Project>
|
|
44
|
+
<PropertyGroup>
|
|
45
|
+
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
|
46
|
+
</PropertyGroup>
|
|
47
|
+
<ItemGroup>
|
|
48
|
+
<PackageVersion Include="MediatR" Version="12.4.1" />
|
|
49
|
+
<PackageVersion Include="FluentValidation" Version="11.11.0" />
|
|
50
|
+
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
|
|
51
|
+
</ItemGroup>
|
|
52
|
+
</Project>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Idioms / Code Style
|
|
58
|
+
|
|
59
|
+
### Nullable Reference Types
|
|
60
|
+
```csharp
|
|
61
|
+
public class Order
|
|
62
|
+
{
|
|
63
|
+
public required string Id { get; init; }
|
|
64
|
+
public string? Notes { get; set; } // explicitly nullable
|
|
65
|
+
public required Customer Customer { get; init; } // never null
|
|
66
|
+
public string Summary => $"Order {Id}: {Notes ?? "no notes"}";
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Records
|
|
71
|
+
```csharp
|
|
72
|
+
public record Money(decimal Amount, string Currency)
|
|
73
|
+
{
|
|
74
|
+
public Money Add(Money other) => Currency != other.Currency
|
|
75
|
+
? throw new InvalidOperationException("Currency mismatch")
|
|
76
|
+
: this with { Amount = Amount + other.Amount };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public record CreateOrderRequest(string CustomerId, List<OrderLineDto> Lines);
|
|
80
|
+
public record OrderLineDto(string ProductId, int Quantity);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Pattern Matching
|
|
84
|
+
```csharp
|
|
85
|
+
public decimal CalculateDiscount(Customer c) => c switch
|
|
86
|
+
{
|
|
87
|
+
{ Tier: CustomerTier.Gold, TotalSpent: > 10_000m } => 0.20m,
|
|
88
|
+
{ Tier: CustomerTier.Gold } => 0.15m,
|
|
89
|
+
{ Tier: CustomerTier.Silver } => 0.10m,
|
|
90
|
+
{ IsNewCustomer: true } => 0.05m,
|
|
91
|
+
_ => 0m
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// List patterns (.NET 8+)
|
|
95
|
+
public string Describe(int[] v) => v switch
|
|
96
|
+
{
|
|
97
|
+
[] => "empty", [var x] => $"one: {x}", [var f, .., var l] => $"{f}..{l}",
|
|
98
|
+
};
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### LINQ
|
|
102
|
+
```csharp
|
|
103
|
+
var activeUsers = users
|
|
104
|
+
.Where(u => u.IsActive)
|
|
105
|
+
.OrderByDescending(u => u.LastLogin)
|
|
106
|
+
.Select(u => new UserDto(u.Id, u.Name))
|
|
107
|
+
.ToList();
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Async/Await
|
|
111
|
+
```csharp
|
|
112
|
+
// Always: Async suffix, CancellationToken parameter, never .Result/.Wait()
|
|
113
|
+
public async Task<Order?> GetOrderAsync(string id, CancellationToken ct = default)
|
|
114
|
+
=> await _db.Orders.Include(o => o.Lines).FirstOrDefaultAsync(o => o.Id == id, ct);
|
|
115
|
+
|
|
116
|
+
// Parallel independent tasks
|
|
117
|
+
var ordersTask = _orderRepo.GetRecentAsync(ct);
|
|
118
|
+
var statsTask = _statsService.ComputeAsync(ct);
|
|
119
|
+
await Task.WhenAll(ordersTask, statsTask);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Primary Constructors (C# 12)
|
|
123
|
+
```csharp
|
|
124
|
+
public class OrderService(IOrderRepository repo, ILogger<OrderService> logger, IPublisher pub)
|
|
125
|
+
{
|
|
126
|
+
public async Task<Order> CreateAsync(CreateOrderRequest req, CancellationToken ct)
|
|
127
|
+
{
|
|
128
|
+
logger.LogInformation("Creating order for {CustomerId}", req.CustomerId);
|
|
129
|
+
var order = Order.Create(req);
|
|
130
|
+
await repo.AddAsync(order, ct);
|
|
131
|
+
await pub.Publish(new OrderCreatedEvent(order.Id), ct);
|
|
132
|
+
return order;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Error Handling
|
|
140
|
+
|
|
141
|
+
### Result Pattern
|
|
142
|
+
```csharp
|
|
143
|
+
public sealed class Result<T>
|
|
144
|
+
{
|
|
145
|
+
public T? Value { get; }
|
|
146
|
+
public Error? Error { get; }
|
|
147
|
+
public bool IsSuccess => Error is null;
|
|
148
|
+
private Result(T value) => Value = value;
|
|
149
|
+
private Result(Error error) => Error = error;
|
|
150
|
+
public static Result<T> Success(T value) => new(value);
|
|
151
|
+
public static Result<T> Failure(Error error) => new(error);
|
|
152
|
+
public TOut Match<TOut>(Func<T, TOut> ok, Func<Error, TOut> err) =>
|
|
153
|
+
IsSuccess ? ok(Value!) : err(Error!);
|
|
154
|
+
}
|
|
155
|
+
public record Error(string Code, string Message);
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### FluentValidation + MediatR Pipeline
|
|
159
|
+
```csharp
|
|
160
|
+
public class CreateOrderValidator : AbstractValidator<CreateOrderRequest>
|
|
161
|
+
{
|
|
162
|
+
public CreateOrderValidator()
|
|
163
|
+
{
|
|
164
|
+
RuleFor(x => x.CustomerId).NotEmpty().MaximumLength(36);
|
|
165
|
+
RuleForEach(x => x.Lines).ChildRules(line =>
|
|
166
|
+
{
|
|
167
|
+
line.RuleFor(l => l.ProductId).NotEmpty();
|
|
168
|
+
line.RuleFor(l => l.Quantity).GreaterThan(0).LessThanOrEqualTo(1000);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
public class ValidationBehavior<TReq, TRes>(IEnumerable<IValidator<TReq>> validators)
|
|
174
|
+
: IPipelineBehavior<TReq, TRes> where TReq : IRequest<TRes>
|
|
175
|
+
{
|
|
176
|
+
public async Task<TRes> Handle(TReq req, RequestHandlerDelegate<TRes> next, CancellationToken ct)
|
|
177
|
+
{
|
|
178
|
+
var failures = validators.Select(v => v.Validate(req)).SelectMany(r => r.Errors).ToList();
|
|
179
|
+
return failures.Count > 0 ? throw new ValidationException(failures) : await next();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### IAsyncDisposable
|
|
185
|
+
```csharp
|
|
186
|
+
public sealed class TempFileHandle(string path) : IAsyncDisposable
|
|
187
|
+
{
|
|
188
|
+
public async ValueTask DisposeAsync()
|
|
189
|
+
{
|
|
190
|
+
if (File.Exists(path)) await Task.Run(() => File.Delete(path));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Usage: await using var handle = new TempFileHandle("/tmp/export.csv");
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Testing Patterns
|
|
199
|
+
|
|
200
|
+
### xUnit + NSubstitute + FluentAssertions
|
|
201
|
+
```csharp
|
|
202
|
+
public class OrderServiceTests
|
|
203
|
+
{
|
|
204
|
+
private readonly IOrderRepository _repo = Substitute.For<IOrderRepository>();
|
|
205
|
+
private readonly IPublisher _pub = Substitute.For<IPublisher>();
|
|
206
|
+
private readonly OrderService _sut;
|
|
207
|
+
public OrderServiceTests() => _sut = new(_repo, Substitute.For<ILogger<OrderService>>(), _pub);
|
|
208
|
+
|
|
209
|
+
[Fact]
|
|
210
|
+
public async Task CreateAsync_ValidRequest_ReturnsOrder()
|
|
211
|
+
{
|
|
212
|
+
var order = await _sut.CreateAsync(new("cust-1", [new("prod-1", 2)]), CancellationToken.None);
|
|
213
|
+
order.Should().NotBeNull();
|
|
214
|
+
order.Lines.Should().ContainSingle().Which.Quantity.Should().Be(2);
|
|
215
|
+
await _repo.Received(1).AddAsync(Arg.Any<Order>(), Arg.Any<CancellationToken>());
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
[Theory]
|
|
219
|
+
[InlineData(0)]
|
|
220
|
+
[InlineData(-1)]
|
|
221
|
+
public async Task CreateAsync_InvalidQuantity_Throws(int qty)
|
|
222
|
+
{
|
|
223
|
+
var act = () => _sut.CreateAsync(new("cust-1", [new("prod-1", qty)]), CancellationToken.None);
|
|
224
|
+
await act.Should().ThrowAsync<ValidationException>();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### WebApplicationFactory (Integration)
|
|
230
|
+
```csharp
|
|
231
|
+
public class OrdersApiTests(WebApplicationFactory<Program> factory)
|
|
232
|
+
: IClassFixture<WebApplicationFactory<Program>>
|
|
233
|
+
{
|
|
234
|
+
[Fact]
|
|
235
|
+
public async Task PostOrder_Returns201()
|
|
236
|
+
{
|
|
237
|
+
var client = factory.WithWebHostBuilder(b => b.ConfigureServices(s =>
|
|
238
|
+
{
|
|
239
|
+
s.RemoveAll<DbContext>();
|
|
240
|
+
s.AddDbContext<AppDbContext>(o => o.UseInMemoryDatabase("test"));
|
|
241
|
+
})).CreateClient();
|
|
242
|
+
|
|
243
|
+
var response = await client.PostAsJsonAsync("/api/orders",
|
|
244
|
+
new { CustomerId = "cust-1", Lines = new[] { new { ProductId = "prod-1", Quantity = 2 } } });
|
|
245
|
+
response.StatusCode.Should().Be(HttpStatusCode.Created);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Testcontainers
|
|
251
|
+
```csharp
|
|
252
|
+
public class PostgresFixture : IAsyncLifetime
|
|
253
|
+
{
|
|
254
|
+
private readonly PostgreSqlContainer _pg = new PostgreSqlBuilder().WithDatabase("testdb").Build();
|
|
255
|
+
public string ConnectionString => _pg.GetConnectionString();
|
|
256
|
+
public Task InitializeAsync() => _pg.StartAsync();
|
|
257
|
+
public Task DisposeAsync() => _pg.DisposeAsync().AsTask();
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Common Frameworks
|
|
264
|
+
|
|
265
|
+
### ASP.NET Core Minimal API
|
|
266
|
+
```csharp
|
|
267
|
+
var builder = WebApplication.CreateBuilder(args);
|
|
268
|
+
builder.Services.AddEndpointsApiExplorer();
|
|
269
|
+
builder.Services.AddSwaggerGen();
|
|
270
|
+
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));
|
|
271
|
+
var app = builder.Build();
|
|
272
|
+
|
|
273
|
+
app.MapGet("/api/orders/{id}", async (string id, ISender sender, CancellationToken ct) =>
|
|
274
|
+
await sender.Send(new GetOrderQuery(id), ct) is { } order ? Results.Ok(order) : Results.NotFound());
|
|
275
|
+
|
|
276
|
+
app.MapPost("/api/orders", async (CreateOrderRequest req, ISender sender, CancellationToken ct) =>
|
|
277
|
+
(await sender.Send(new CreateOrderCommand(req), ct)).Match(
|
|
278
|
+
o => Results.Created($"/api/orders/{o.Id}", o),
|
|
279
|
+
e => Results.Problem(e.Message, statusCode: 400)));
|
|
280
|
+
app.Run();
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Entity Framework Core
|
|
284
|
+
```csharp
|
|
285
|
+
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
|
|
286
|
+
{
|
|
287
|
+
public DbSet<Order> Orders => Set<Order>();
|
|
288
|
+
protected override void OnModelCreating(ModelBuilder mb)
|
|
289
|
+
{
|
|
290
|
+
mb.Entity<Order>(b =>
|
|
291
|
+
{
|
|
292
|
+
b.HasKey(o => o.Id);
|
|
293
|
+
b.Property(o => o.Id).HasMaxLength(36);
|
|
294
|
+
b.HasMany(o => o.Lines).WithOne().HasForeignKey(l => l.OrderId);
|
|
295
|
+
b.HasIndex(o => o.CustomerId);
|
|
296
|
+
b.Property(o => o.Total).HasPrecision(18, 2);
|
|
297
|
+
});
|
|
298
|
+
mb.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### MediatR (CQRS)
|
|
304
|
+
```csharp
|
|
305
|
+
public record CreateOrderCommand(CreateOrderRequest Request) : IRequest<Result<Order>>;
|
|
306
|
+
|
|
307
|
+
public class CreateOrderHandler(IOrderRepository repo, IPublisher pub)
|
|
308
|
+
: IRequestHandler<CreateOrderCommand, Result<Order>>
|
|
309
|
+
{
|
|
310
|
+
public async Task<Result<Order>> Handle(CreateOrderCommand cmd, CancellationToken ct)
|
|
311
|
+
{
|
|
312
|
+
var order = Order.Create(cmd.Request);
|
|
313
|
+
await repo.AddAsync(order, ct);
|
|
314
|
+
await pub.Publish(new OrderCreatedEvent(order.Id), ct);
|
|
315
|
+
return Result<Order>.Success(order);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Polly (Resilience)
|
|
321
|
+
```csharp
|
|
322
|
+
builder.Services.AddHttpClient("Payments", c => c.BaseAddress = new Uri("https://api.payments.com"))
|
|
323
|
+
.AddResilienceHandler("default", p =>
|
|
324
|
+
{
|
|
325
|
+
p.AddRetry(new() { MaxRetryAttempts = 3, Delay = TimeSpan.FromMilliseconds(500),
|
|
326
|
+
BackoffType = DelayBackoffType.Exponential,
|
|
327
|
+
ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
|
|
328
|
+
.HandleResult(r => r.StatusCode >= HttpStatusCode.InternalServerError) });
|
|
329
|
+
p.AddCircuitBreaker(new() { FailureRatio = 0.5, SamplingDuration = TimeSpan.FromSeconds(30),
|
|
330
|
+
BreakDuration = TimeSpan.FromSeconds(15) });
|
|
331
|
+
p.AddTimeout(TimeSpan.FromSeconds(5));
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### MassTransit (Messaging)
|
|
336
|
+
```csharp
|
|
337
|
+
builder.Services.AddMassTransit(x =>
|
|
338
|
+
{
|
|
339
|
+
x.AddConsumer<OrderCreatedConsumer>();
|
|
340
|
+
x.UsingRabbitMq((ctx, cfg) => { cfg.Host("rabbitmq://localhost"); cfg.ConfigureEndpoints(ctx); });
|
|
341
|
+
});
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## Performance Tips
|
|
347
|
+
|
|
348
|
+
### Span<T> -- Zero-Allocation Parsing
|
|
349
|
+
```csharp
|
|
350
|
+
public static ReadOnlySpan<char> ExtractDomain(ReadOnlySpan<char> email)
|
|
351
|
+
{
|
|
352
|
+
int at = email.IndexOf('@');
|
|
353
|
+
return at >= 0 ? email[(at + 1)..] : ReadOnlySpan<char>.Empty;
|
|
354
|
+
}
|
|
355
|
+
// Stack-allocated buffer: Span<byte> buf = stackalloc byte[256];
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### ValueTask -- Cache-Hit Fast Path
|
|
359
|
+
```csharp
|
|
360
|
+
public ValueTask<Product?> GetProductAsync(string id, CancellationToken ct)
|
|
361
|
+
{
|
|
362
|
+
if (_cache.TryGetValue(id, out var cached)) return ValueTask.FromResult<Product?>(cached);
|
|
363
|
+
return new ValueTask<Product?>(LoadFromDbAsync(id, ct));
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Source Generators
|
|
368
|
+
```csharp
|
|
369
|
+
// JSON -- avoids runtime reflection
|
|
370
|
+
[JsonSerializable(typeof(Order))]
|
|
371
|
+
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
|
|
372
|
+
public partial class AppJsonContext : JsonSerializerContext;
|
|
373
|
+
|
|
374
|
+
// Logging -- avoids boxing
|
|
375
|
+
public static partial class Log
|
|
376
|
+
{
|
|
377
|
+
[LoggerMessage(Level = LogLevel.Information, Message = "Order {OrderId} created")]
|
|
378
|
+
public static partial void OrderCreated(ILogger logger, string orderId);
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### BenchmarkDotNet
|
|
383
|
+
```csharp
|
|
384
|
+
[MemoryDiagnoser, SimpleJob(RuntimeMoniker.Net90)]
|
|
385
|
+
public class ParsingBenchmarks
|
|
386
|
+
{
|
|
387
|
+
private readonly string _email = "user@example.com";
|
|
388
|
+
[Benchmark(Baseline = true)] public string Sub() => _email[(_email.IndexOf('@') + 1)..];
|
|
389
|
+
[Benchmark] public ReadOnlySpan<char> Span() => ExtractDomain(_email.AsSpan());
|
|
390
|
+
}
|
|
391
|
+
// dotnet run -c Release --project Benchmarks
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## Build / Package Management
|
|
397
|
+
|
|
398
|
+
### dotnet CLI
|
|
399
|
+
```bash
|
|
400
|
+
dotnet new sln -n MyApp && dotnet new webapi -n MyApp.Api -o src/MyApp.Api
|
|
401
|
+
dotnet build -c Release
|
|
402
|
+
dotnet test -c Release --collect:"XPlat Code Coverage"
|
|
403
|
+
dotnet publish src/MyApp.Api -c Release -o ./publish
|
|
404
|
+
dotnet ef migrations add Init --project src/MyApp.Infrastructure --startup-project src/MyApp.Api
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### .editorconfig
|
|
408
|
+
```ini
|
|
409
|
+
[*.cs]
|
|
410
|
+
dotnet_sort_system_directives_first = true
|
|
411
|
+
csharp_style_namespace_declarations = file_scoped:error
|
|
412
|
+
csharp_prefer_primary_constructors = true:suggestion
|
|
413
|
+
dotnet_diagnostic.CA1848.severity = warning
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### CI (GitHub Actions)
|
|
417
|
+
```yaml
|
|
418
|
+
steps:
|
|
419
|
+
- uses: actions/checkout@v4
|
|
420
|
+
- uses: actions/setup-dotnet@v4
|
|
421
|
+
with: { dotnet-version: '9.0.x' }
|
|
422
|
+
- run: dotnet restore
|
|
423
|
+
- run: dotnet build --no-restore -c Release -warnaserror
|
|
424
|
+
- run: dotnet test --no-build -c Release --collect:"XPlat Code Coverage"
|
|
425
|
+
- run: dotnet publish src/MyApp.Api -c Release -o publish --no-build
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Dockerfile
|
|
429
|
+
```dockerfile
|
|
430
|
+
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
|
431
|
+
WORKDIR /src
|
|
432
|
+
COPY . .
|
|
433
|
+
RUN dotnet publish src/MyApp.Api -c Release -o /app
|
|
434
|
+
FROM mcr.microsoft.com/dotnet/aspnet:9.0
|
|
435
|
+
COPY --from=build /app /app
|
|
436
|
+
WORKDIR /app
|
|
437
|
+
ENTRYPOINT ["dotnet", "MyApp.Api.dll"]
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## Anti-Patterns
|
|
443
|
+
- `async void` outside event handlers -- always return `Task`
|
|
444
|
+
- Blocking with `.Result` / `.Wait()` -- deadlock risk
|
|
445
|
+
- Missing `CancellationToken` propagation through the call chain
|
|
446
|
+
- Catching bare `Exception` without re-throw or structured logging
|
|
447
|
+
- Service locator via `IServiceProvider.GetService` in business logic
|
|
448
|
+
- Exposing `IQueryable` from repositories -- leaks persistence details
|
|
449
|
+
- Public setters on domain entities -- use methods + private setters
|
|
450
|
+
- String concatenation in hot paths -- use `StringBuilder` or string handlers
|