@zimezone/z-command 1.0.1 → 1.1.1

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.
Files changed (217) hide show
  1. package/README.md +57 -38
  2. package/dist/cli.js +14 -2
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/init.d.ts +1 -7
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/init.js +101 -23
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/update.d.ts +11 -0
  9. package/dist/commands/update.d.ts.map +1 -0
  10. package/dist/commands/update.js +88 -0
  11. package/dist/commands/update.js.map +1 -0
  12. package/dist/platforms.d.ts +21 -0
  13. package/dist/platforms.d.ts.map +1 -0
  14. package/dist/platforms.js +137 -0
  15. package/dist/platforms.js.map +1 -0
  16. package/dist/types.d.ts +44 -0
  17. package/dist/types.d.ts.map +1 -0
  18. package/dist/types.js +6 -0
  19. package/dist/types.js.map +1 -0
  20. package/package.json +13 -5
  21. package/templates/agents/api-documenter.agent.md +161 -0
  22. package/templates/agents/architect-review.agent.md +146 -0
  23. package/templates/agents/arm-cortex-expert.agent.md +288 -0
  24. package/templates/agents/backend-architect.agent.md +309 -0
  25. package/templates/agents/backend-security-coder.agent.md +152 -0
  26. package/templates/agents/bash-pro.agent.md +285 -0
  27. package/templates/agents/c-pro.agent.md +35 -0
  28. package/templates/agents/c4-code.agent.md +320 -0
  29. package/templates/agents/c4-component.agent.md +227 -0
  30. package/templates/agents/c4-container.agent.md +248 -0
  31. package/templates/agents/c4-context.agent.md +235 -0
  32. package/templates/agents/conductor-validator.agent.md +245 -0
  33. package/templates/agents/csharp-pro.agent.md +38 -0
  34. package/templates/agents/customer-support.agent.md +148 -0
  35. package/templates/agents/database-admin.agent.md +142 -0
  36. package/templates/agents/database-architect.agent.md +238 -0
  37. package/templates/agents/database-optimizer.agent.md +144 -0
  38. package/templates/agents/debugger.agent.md +30 -0
  39. package/templates/agents/deployment-engineer.agent.md +0 -0
  40. package/templates/agents/devops-troubleshooter.agent.md +138 -0
  41. package/templates/agents/django-pro.agent.md +159 -0
  42. package/templates/agents/docs-architect.agent.md +77 -0
  43. package/templates/agents/dotnet-architect.agent.md +175 -0
  44. package/templates/agents/dx-optimizer.agent.md +63 -0
  45. package/templates/agents/elixir-pro.agent.md +38 -0
  46. package/templates/agents/error-detective.agent.md +32 -0
  47. package/templates/agents/event-sourcing-architect.agent.md +42 -0
  48. package/templates/agents/fastapi-pro.agent.md +171 -0
  49. package/templates/agents/firmware-analyst.agent.md +330 -0
  50. package/templates/agents/frontend-security-coder.agent.md +149 -0
  51. package/templates/agents/haskell-pro.agent.md +37 -0
  52. package/templates/agents/hr-pro.agent.md +105 -0
  53. package/templates/agents/incident-responder.agent.md +190 -0
  54. package/templates/agents/ios-developer.agent.md +198 -0
  55. package/templates/agents/java-pro.agent.md +156 -0
  56. package/templates/agents/javascript-pro.agent.md +35 -0
  57. package/templates/agents/julia-pro.agent.md +187 -0
  58. package/templates/agents/legal-advisor.agent.md +49 -0
  59. package/templates/agents/malware-analyst.agent.md +272 -0
  60. package/templates/agents/mermaid-expert.agent.md +39 -0
  61. package/templates/agents/minecraft-bukkit-pro.agent.md +104 -0
  62. package/templates/agents/mobile-security-coder.agent.md +163 -0
  63. package/templates/agents/monorepo-architect.agent.md +44 -0
  64. package/templates/agents/observability-engineer.agent.md +228 -0
  65. package/templates/agents/performance-engineer.agent.md +167 -0
  66. package/templates/agents/php-pro.agent.md +43 -0
  67. package/templates/agents/posix-shell-pro.agent.md +284 -0
  68. package/templates/agents/quant-analyst.agent.md +32 -0
  69. package/templates/agents/reference-builder.agent.md +167 -0
  70. package/templates/agents/reverse-engineer.agent.md +202 -0
  71. package/templates/agents/risk-manager.agent.md +41 -0
  72. package/templates/agents/ruby-pro.agent.md +35 -0
  73. package/templates/agents/rust-pro.agent.md +156 -0
  74. package/templates/agents/sales-automator.agent.md +35 -0
  75. package/templates/agents/scala-pro.agent.md +60 -0
  76. package/templates/agents/search-specialist.agent.md +59 -0
  77. package/templates/agents/security-auditor.agent.md +138 -0
  78. package/templates/agents/seo-authority-builder.agent.md +116 -0
  79. package/templates/agents/seo-cannibalization-detector.agent.md +103 -0
  80. package/templates/agents/seo-content-auditor.agent.md +63 -0
  81. package/templates/agents/seo-content-planner.agent.md +88 -0
  82. package/templates/agents/seo-content-refresher.agent.md +98 -0
  83. package/templates/agents/seo-content-writer.agent.md +76 -0
  84. package/templates/agents/seo-keyword-strategist.agent.md +75 -0
  85. package/templates/agents/seo-meta-optimizer.agent.md +72 -0
  86. package/templates/agents/seo-snippet-hunter.agent.md +94 -0
  87. package/templates/agents/seo-structure-architect.agent.md +88 -0
  88. package/templates/agents/service-mesh-expert.agent.md +41 -0
  89. package/templates/agents/sql-pro.agent.md +146 -0
  90. package/templates/agents/tdd-orchestrator.agent.md +183 -0
  91. package/templates/agents/temporal-python-pro.agent.md +349 -0
  92. package/templates/agents/terraform-specialist.agent.md +137 -0
  93. package/templates/agents/test-automator.agent.md +203 -0
  94. package/templates/agents/threat-modeling-expert.agent.md +44 -0
  95. package/templates/agents/tutorial-engineer.agent.md +118 -0
  96. package/templates/agents/ui-ux-designer.agent.md +188 -0
  97. package/templates/agents/ui-visual-validator.agent.md +192 -0
  98. package/templates/agents/vector-database-engineer.agent.md +43 -0
  99. package/templates/skills/angular-migration/SKILL.md +410 -0
  100. package/templates/skills/api-design-principles/SKILL.md +528 -0
  101. package/templates/skills/api-design-principles/assets/api-design-checklist.md +155 -0
  102. package/templates/skills/api-design-principles/assets/rest-api-template.py +182 -0
  103. package/templates/skills/api-design-principles/references/graphql-schema-design.md +583 -0
  104. package/templates/skills/api-design-principles/references/rest-best-practices.md +408 -0
  105. package/templates/skills/architecture-decision-records/SKILL.md +428 -0
  106. package/templates/skills/architecture-patterns/SKILL.md +494 -0
  107. package/templates/skills/async-python-patterns/SKILL.md +694 -0
  108. package/templates/skills/auth-implementation-patterns/SKILL.md +634 -0
  109. package/templates/skills/changelog-automation/SKILL.md +552 -0
  110. package/templates/skills/code-review-excellence/SKILL.md +520 -0
  111. package/templates/skills/competitive-landscape/SKILL.md +479 -0
  112. package/templates/skills/context-driven-development/SKILL.md +385 -0
  113. package/templates/skills/cost-optimization/SKILL.md +274 -0
  114. package/templates/skills/cqrs-implementation/SKILL.md +554 -0
  115. package/templates/skills/data-quality-frameworks/SKILL.md +587 -0
  116. package/templates/skills/data-storytelling/SKILL.md +453 -0
  117. package/templates/skills/database-migration/SKILL.md +424 -0
  118. package/templates/skills/dbt-transformation-patterns/SKILL.md +561 -0
  119. package/templates/skills/debugging-strategies/SKILL.md +527 -0
  120. package/templates/skills/defi-protocol-templates/SKILL.md +454 -0
  121. package/templates/skills/dependency-upgrade/SKILL.md +409 -0
  122. package/templates/skills/deployment-pipeline-design/SKILL.md +359 -0
  123. package/templates/skills/distributed-tracing/SKILL.md +438 -0
  124. package/templates/skills/dotnet-backend-patterns/SKILL.md +815 -0
  125. package/templates/skills/dotnet-backend-patterns/assets/repository-template.cs +523 -0
  126. package/templates/skills/dotnet-backend-patterns/assets/service-template.cs +336 -0
  127. package/templates/skills/dotnet-backend-patterns/references/dapper-patterns.md +544 -0
  128. package/templates/skills/dotnet-backend-patterns/references/ef-core-best-practices.md +355 -0
  129. package/templates/skills/e2e-testing-patterns/SKILL.md +547 -0
  130. package/templates/skills/employment-contract-templates/SKILL.md +507 -0
  131. package/templates/skills/error-handling-patterns/SKILL.md +636 -0
  132. package/templates/skills/event-store-design/SKILL.md +437 -0
  133. package/templates/skills/fastapi-templates/SKILL.md +567 -0
  134. package/templates/skills/git-advanced-workflows/SKILL.md +400 -0
  135. package/templates/skills/github-actions-templates/SKILL.md +333 -0
  136. package/templates/skills/go-concurrency-patterns/SKILL.md +655 -0
  137. package/templates/skills/grafana-dashboards/SKILL.md +369 -0
  138. package/templates/skills/helm-chart-scaffolding/SKILL.md +544 -0
  139. package/templates/skills/helm-chart-scaffolding/assets/Chart.yaml.template +42 -0
  140. package/templates/skills/helm-chart-scaffolding/assets/values.yaml.template +185 -0
  141. package/templates/skills/helm-chart-scaffolding/references/chart-structure.md +500 -0
  142. package/templates/skills/helm-chart-scaffolding/scripts/validate-chart.sh +244 -0
  143. package/templates/skills/javascript-testing-patterns/SKILL.md +1025 -0
  144. package/templates/skills/langchain-architecture/SKILL.md +338 -0
  145. package/templates/skills/llm-evaluation/SKILL.md +471 -0
  146. package/templates/skills/microservices-patterns/SKILL.md +595 -0
  147. package/templates/skills/modern-javascript-patterns/SKILL.md +911 -0
  148. package/templates/skills/monorepo-management/SKILL.md +622 -0
  149. package/templates/skills/nextjs-app-router-patterns/SKILL.md +544 -0
  150. package/templates/skills/nodejs-backend-patterns/SKILL.md +1020 -0
  151. package/templates/skills/nx-workspace-patterns/SKILL.md +452 -0
  152. package/templates/skills/openapi-spec-generation/SKILL.md +1028 -0
  153. package/templates/skills/paypal-integration/SKILL.md +467 -0
  154. package/templates/skills/pci-compliance/SKILL.md +466 -0
  155. package/templates/skills/postgresql/SKILL.md +204 -0
  156. package/templates/skills/projection-patterns/SKILL.md +490 -0
  157. package/templates/skills/prometheus-configuration/SKILL.md +392 -0
  158. package/templates/skills/prompt-engineering-patterns/SKILL.md +201 -0
  159. package/templates/skills/prompt-engineering-patterns/assets/few-shot-examples.json +106 -0
  160. package/templates/skills/prompt-engineering-patterns/assets/prompt-template-library.md +246 -0
  161. package/templates/skills/prompt-engineering-patterns/references/chain-of-thought.md +399 -0
  162. package/templates/skills/prompt-engineering-patterns/references/few-shot-learning.md +369 -0
  163. package/templates/skills/prompt-engineering-patterns/references/prompt-optimization.md +414 -0
  164. package/templates/skills/prompt-engineering-patterns/references/prompt-templates.md +470 -0
  165. package/templates/skills/prompt-engineering-patterns/references/system-prompts.md +189 -0
  166. package/templates/skills/prompt-engineering-patterns/scripts/optimize-prompt.py +279 -0
  167. package/templates/skills/python-packaging/SKILL.md +870 -0
  168. package/templates/skills/python-performance-optimization/SKILL.md +869 -0
  169. package/templates/skills/python-testing-patterns/SKILL.md +907 -0
  170. package/templates/skills/rag-implementation/SKILL.md +403 -0
  171. package/templates/skills/react-modernization/SKILL.md +513 -0
  172. package/templates/skills/react-native-architecture/SKILL.md +671 -0
  173. package/templates/skills/react-state-management/SKILL.md +429 -0
  174. package/templates/skills/risk-metrics-calculation/SKILL.md +555 -0
  175. package/templates/skills/rust-async-patterns/SKILL.md +517 -0
  176. package/templates/skills/secrets-management/SKILL.md +346 -0
  177. package/templates/skills/security-requirement-extraction/SKILL.md +677 -0
  178. package/templates/skills/shellcheck-configuration/SKILL.md +454 -0
  179. package/templates/skills/similarity-search-patterns/SKILL.md +558 -0
  180. package/templates/skills/slo-implementation/SKILL.md +329 -0
  181. package/templates/skills/sql-optimization-patterns/SKILL.md +493 -0
  182. package/templates/skills/stripe-integration/SKILL.md +442 -0
  183. package/templates/skills/tailwind-design-system/SKILL.md +666 -0
  184. package/templates/skills/temporal-python-testing/SKILL.md +158 -0
  185. package/templates/skills/temporal-python-testing/resources/integration-testing.md +455 -0
  186. package/templates/skills/temporal-python-testing/resources/local-setup.md +553 -0
  187. package/templates/skills/temporal-python-testing/resources/replay-testing.md +462 -0
  188. package/templates/skills/temporal-python-testing/resources/unit-testing.md +328 -0
  189. package/templates/skills/terraform-module-library/SKILL.md +249 -0
  190. package/templates/skills/terraform-module-library/references/aws-modules.md +63 -0
  191. package/templates/skills/threat-mitigation-mapping/SKILL.md +745 -0
  192. package/templates/skills/track-management/SKILL.md +593 -0
  193. package/templates/skills/typescript-advanced-types/SKILL.md +717 -0
  194. package/templates/skills/ui-ux-pro-max/SKILL.md +352 -0
  195. package/templates/skills/ui-ux-pro-max/data/charts.csv +26 -0
  196. package/templates/skills/ui-ux-pro-max/data/colors.csv +97 -0
  197. package/templates/skills/ui-ux-pro-max/data/icons.csv +101 -0
  198. package/templates/skills/ui-ux-pro-max/data/landing.csv +31 -0
  199. package/templates/skills/ui-ux-pro-max/data/products.csv +97 -0
  200. package/templates/skills/ui-ux-pro-max/data/prompts.csv +24 -0
  201. package/templates/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  202. package/templates/skills/ui-ux-pro-max/data/styles.csv +59 -0
  203. package/templates/skills/ui-ux-pro-max/data/typography.csv +58 -0
  204. package/templates/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  205. package/templates/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  206. package/templates/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
  207. package/templates/skills/ui-ux-pro-max/scripts/core.py +258 -0
  208. package/templates/skills/ui-ux-pro-max/scripts/design_system.py +547 -0
  209. package/templates/skills/ui-ux-pro-max/scripts/search.py +76 -0
  210. package/templates/skills/uv-package-manager/SKILL.md +831 -0
  211. package/templates/skills/vector-index-tuning/SKILL.md +521 -0
  212. package/templates/skills/wcag-audit-patterns/SKILL.md +555 -0
  213. package/templates/skills/workflow-orchestration-patterns/SKILL.md +316 -0
  214. package/templates/skills/workflow-patterns/SKILL.md +623 -0
  215. package/templates/agents/game-developer.agent.md +0 -57
  216. package/templates/agents/kubernetes-specialist.agent.md +0 -56
  217. package/templates/agents/market-researcher.agent.md +0 -47
@@ -0,0 +1,815 @@
1
+ ---
2
+ name: dotnet-backend-patterns
3
+ description: Master C#/.NET backend development patterns for building robust APIs, MCP servers, and enterprise applications. Covers async/await, dependency injection, Entity Framework Core, Dapper, configuration, caching, and testing with xUnit. Use when developing .NET backends, reviewing C# code, or designing API architectures.
4
+ ---
5
+
6
+ # .NET Backend Development Patterns
7
+
8
+ Master C#/.NET patterns for building production-grade APIs, MCP servers, and enterprise backends with modern best practices (2024/2025).
9
+
10
+ ## When to Use This Skill
11
+
12
+ - Developing new .NET Web APIs or MCP servers
13
+ - Reviewing C# code for quality and performance
14
+ - Designing service architectures with dependency injection
15
+ - Implementing caching strategies with Redis
16
+ - Writing unit and integration tests
17
+ - Optimizing database access with EF Core or Dapper
18
+ - Configuring applications with IOptions pattern
19
+ - Handling errors and implementing resilience patterns
20
+
21
+ ## Core Concepts
22
+
23
+ ### 1. Project Structure (Clean Architecture)
24
+
25
+ ```
26
+ src/
27
+ ├── Domain/ # Core business logic (no dependencies)
28
+ │ ├── Entities/
29
+ │ ├── Interfaces/
30
+ │ ├── Exceptions/
31
+ │ └── ValueObjects/
32
+ ├── Application/ # Use cases, DTOs, validation
33
+ │ ├── Services/
34
+ │ ├── DTOs/
35
+ │ ├── Validators/
36
+ │ └── Interfaces/
37
+ ├── Infrastructure/ # External implementations
38
+ │ ├── Data/ # EF Core, Dapper repositories
39
+ │ ├── Caching/ # Redis, Memory cache
40
+ │ ├── External/ # HTTP clients, third-party APIs
41
+ │ └── DependencyInjection/ # Service registration
42
+ └── Api/ # Entry point
43
+ ├── Controllers/ # Or MinimalAPI endpoints
44
+ ├── Middleware/
45
+ ├── Filters/
46
+ └── Program.cs
47
+ ```
48
+
49
+ ### 2. Dependency Injection Patterns
50
+
51
+ ```csharp
52
+ // Service registration by lifetime
53
+ public static class ServiceCollectionExtensions
54
+ {
55
+ public static IServiceCollection AddApplicationServices(
56
+ this IServiceCollection services,
57
+ IConfiguration configuration)
58
+ {
59
+ // Scoped: One instance per HTTP request
60
+ services.AddScoped<IProductService, ProductService>();
61
+ services.AddScoped<IOrderService, OrderService>();
62
+
63
+ // Singleton: One instance for app lifetime
64
+ services.AddSingleton<ICacheService, RedisCacheService>();
65
+ services.AddSingleton<IConnectionMultiplexer>(_ =>
66
+ ConnectionMultiplexer.Connect(configuration["Redis:Connection"]!));
67
+
68
+ // Transient: New instance every time
69
+ services.AddTransient<IValidator<CreateOrderRequest>, CreateOrderValidator>();
70
+
71
+ // Options pattern for configuration
72
+ services.Configure<CatalogOptions>(configuration.GetSection("Catalog"));
73
+ services.Configure<RedisOptions>(configuration.GetSection("Redis"));
74
+
75
+ // Factory pattern for conditional creation
76
+ services.AddScoped<IPriceCalculator>(sp =>
77
+ {
78
+ var options = sp.GetRequiredService<IOptions<PricingOptions>>().Value;
79
+ return options.UseNewEngine
80
+ ? sp.GetRequiredService<NewPriceCalculator>()
81
+ : sp.GetRequiredService<LegacyPriceCalculator>();
82
+ });
83
+
84
+ // Keyed services (.NET 8+)
85
+ services.AddKeyedScoped<IPaymentProcessor, StripeProcessor>("stripe");
86
+ services.AddKeyedScoped<IPaymentProcessor, PayPalProcessor>("paypal");
87
+
88
+ return services;
89
+ }
90
+ }
91
+
92
+ // Usage with keyed services
93
+ public class CheckoutService
94
+ {
95
+ public CheckoutService(
96
+ [FromKeyedServices("stripe")] IPaymentProcessor stripeProcessor)
97
+ {
98
+ _processor = stripeProcessor;
99
+ }
100
+ }
101
+ ```
102
+
103
+ ### 3. Async/Await Patterns
104
+
105
+ ```csharp
106
+ // ✅ CORRECT: Async all the way down
107
+ public async Task<Product> GetProductAsync(string id, CancellationToken ct = default)
108
+ {
109
+ return await _repository.GetByIdAsync(id, ct);
110
+ }
111
+
112
+ // ✅ CORRECT: Parallel execution with WhenAll
113
+ public async Task<(Stock, Price)> GetStockAndPriceAsync(
114
+ string productId,
115
+ CancellationToken ct = default)
116
+ {
117
+ var stockTask = _stockService.GetAsync(productId, ct);
118
+ var priceTask = _priceService.GetAsync(productId, ct);
119
+
120
+ await Task.WhenAll(stockTask, priceTask);
121
+
122
+ return (await stockTask, await priceTask);
123
+ }
124
+
125
+ // ✅ CORRECT: ConfigureAwait in libraries
126
+ public async Task<T> LibraryMethodAsync<T>(CancellationToken ct = default)
127
+ {
128
+ var result = await _httpClient.GetAsync(url, ct).ConfigureAwait(false);
129
+ return await result.Content.ReadFromJsonAsync<T>(ct).ConfigureAwait(false);
130
+ }
131
+
132
+ // ✅ CORRECT: ValueTask for hot paths with caching
133
+ public ValueTask<Product?> GetCachedProductAsync(string id)
134
+ {
135
+ if (_cache.TryGetValue(id, out Product? product))
136
+ return ValueTask.FromResult(product);
137
+
138
+ return new ValueTask<Product?>(GetFromDatabaseAsync(id));
139
+ }
140
+
141
+ // ❌ WRONG: Blocking on async (deadlock risk)
142
+ var result = GetProductAsync(id).Result; // NEVER do this
143
+ var result2 = GetProductAsync(id).GetAwaiter().GetResult(); // Also bad
144
+
145
+ // ❌ WRONG: async void (except event handlers)
146
+ public async void ProcessOrder() { } // Exceptions are lost
147
+
148
+ // ❌ WRONG: Unnecessary Task.Run for already async code
149
+ await Task.Run(async () => await GetDataAsync()); // Wastes thread
150
+ ```
151
+
152
+ ### 4. Configuration with IOptions
153
+
154
+ ```csharp
155
+ // Configuration classes
156
+ public class CatalogOptions
157
+ {
158
+ public const string SectionName = "Catalog";
159
+
160
+ public int DefaultPageSize { get; set; } = 50;
161
+ public int MaxPageSize { get; set; } = 200;
162
+ public TimeSpan CacheDuration { get; set; } = TimeSpan.FromMinutes(15);
163
+ public bool EnableEnrichment { get; set; } = true;
164
+ }
165
+
166
+ public class RedisOptions
167
+ {
168
+ public const string SectionName = "Redis";
169
+
170
+ public string Connection { get; set; } = "localhost:6379";
171
+ public string KeyPrefix { get; set; } = "mcp:";
172
+ public int Database { get; set; } = 0;
173
+ }
174
+
175
+ // appsettings.json
176
+ {
177
+ "Catalog": {
178
+ "DefaultPageSize": 50,
179
+ "MaxPageSize": 200,
180
+ "CacheDuration": "00:15:00",
181
+ "EnableEnrichment": true
182
+ },
183
+ "Redis": {
184
+ "Connection": "localhost:6379",
185
+ "KeyPrefix": "mcp:",
186
+ "Database": 0
187
+ }
188
+ }
189
+
190
+ // Registration
191
+ services.Configure<CatalogOptions>(configuration.GetSection(CatalogOptions.SectionName));
192
+ services.Configure<RedisOptions>(configuration.GetSection(RedisOptions.SectionName));
193
+
194
+ // Usage with IOptions (singleton, read once at startup)
195
+ public class CatalogService
196
+ {
197
+ private readonly CatalogOptions _options;
198
+
199
+ public CatalogService(IOptions<CatalogOptions> options)
200
+ {
201
+ _options = options.Value;
202
+ }
203
+ }
204
+
205
+ // Usage with IOptionsSnapshot (scoped, re-reads on each request)
206
+ public class DynamicService
207
+ {
208
+ private readonly CatalogOptions _options;
209
+
210
+ public DynamicService(IOptionsSnapshot<CatalogOptions> options)
211
+ {
212
+ _options = options.Value; // Fresh value per request
213
+ }
214
+ }
215
+
216
+ // Usage with IOptionsMonitor (singleton, notified on changes)
217
+ public class MonitoredService
218
+ {
219
+ private CatalogOptions _options;
220
+
221
+ public MonitoredService(IOptionsMonitor<CatalogOptions> monitor)
222
+ {
223
+ _options = monitor.CurrentValue;
224
+ monitor.OnChange(newOptions => _options = newOptions);
225
+ }
226
+ }
227
+ ```
228
+
229
+ ### 5. Result Pattern (Avoiding Exceptions for Flow Control)
230
+
231
+ ```csharp
232
+ // Generic Result type
233
+ public class Result<T>
234
+ {
235
+ public bool IsSuccess { get; }
236
+ public T? Value { get; }
237
+ public string? Error { get; }
238
+ public string? ErrorCode { get; }
239
+
240
+ private Result(bool isSuccess, T? value, string? error, string? errorCode)
241
+ {
242
+ IsSuccess = isSuccess;
243
+ Value = value;
244
+ Error = error;
245
+ ErrorCode = errorCode;
246
+ }
247
+
248
+ public static Result<T> Success(T value) => new(true, value, null, null);
249
+ public static Result<T> Failure(string error, string? code = null) => new(false, default, error, code);
250
+
251
+ public Result<TNew> Map<TNew>(Func<T, TNew> mapper) =>
252
+ IsSuccess ? Result<TNew>.Success(mapper(Value!)) : Result<TNew>.Failure(Error!, ErrorCode);
253
+
254
+ public async Task<Result<TNew>> MapAsync<TNew>(Func<T, Task<TNew>> mapper) =>
255
+ IsSuccess ? Result<TNew>.Success(await mapper(Value!)) : Result<TNew>.Failure(Error!, ErrorCode);
256
+ }
257
+
258
+ // Usage in service
259
+ public async Task<Result<Order>> CreateOrderAsync(CreateOrderRequest request, CancellationToken ct)
260
+ {
261
+ // Validation
262
+ var validation = await _validator.ValidateAsync(request, ct);
263
+ if (!validation.IsValid)
264
+ return Result<Order>.Failure(
265
+ validation.Errors.First().ErrorMessage,
266
+ "VALIDATION_ERROR");
267
+
268
+ // Business rule check
269
+ var stock = await _stockService.CheckAsync(request.ProductId, request.Quantity, ct);
270
+ if (!stock.IsAvailable)
271
+ return Result<Order>.Failure(
272
+ $"Insufficient stock: {stock.Available} available, {request.Quantity} requested",
273
+ "INSUFFICIENT_STOCK");
274
+
275
+ // Create order
276
+ var order = await _repository.CreateAsync(request.ToEntity(), ct);
277
+
278
+ return Result<Order>.Success(order);
279
+ }
280
+
281
+ // Usage in controller/endpoint
282
+ app.MapPost("/orders", async (
283
+ CreateOrderRequest request,
284
+ IOrderService orderService,
285
+ CancellationToken ct) =>
286
+ {
287
+ var result = await orderService.CreateOrderAsync(request, ct);
288
+
289
+ return result.IsSuccess
290
+ ? Results.Created($"/orders/{result.Value!.Id}", result.Value)
291
+ : Results.BadRequest(new { error = result.Error, code = result.ErrorCode });
292
+ });
293
+ ```
294
+
295
+ ## Data Access Patterns
296
+
297
+ ### Entity Framework Core
298
+
299
+ ```csharp
300
+ // DbContext configuration
301
+ public class AppDbContext : DbContext
302
+ {
303
+ public DbSet<Product> Products => Set<Product>();
304
+ public DbSet<Order> Orders => Set<Order>();
305
+
306
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
307
+ {
308
+ // Apply all configurations from assembly
309
+ modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
310
+
311
+ // Global query filters
312
+ modelBuilder.Entity<Product>().HasQueryFilter(p => !p.IsDeleted);
313
+ }
314
+ }
315
+
316
+ // Entity configuration
317
+ public class ProductConfiguration : IEntityTypeConfiguration<Product>
318
+ {
319
+ public void Configure(EntityTypeBuilder<Product> builder)
320
+ {
321
+ builder.ToTable("Products");
322
+
323
+ builder.HasKey(p => p.Id);
324
+ builder.Property(p => p.Id).HasMaxLength(40);
325
+ builder.Property(p => p.Name).HasMaxLength(200).IsRequired();
326
+ builder.Property(p => p.Price).HasPrecision(18, 2);
327
+
328
+ builder.HasIndex(p => p.Sku).IsUnique();
329
+ builder.HasIndex(p => new { p.CategoryId, p.Name });
330
+
331
+ builder.HasMany(p => p.OrderItems)
332
+ .WithOne(oi => oi.Product)
333
+ .HasForeignKey(oi => oi.ProductId);
334
+ }
335
+ }
336
+
337
+ // Repository with EF Core
338
+ public class ProductRepository : IProductRepository
339
+ {
340
+ private readonly AppDbContext _context;
341
+
342
+ public async Task<Product?> GetByIdAsync(string id, CancellationToken ct = default)
343
+ {
344
+ return await _context.Products
345
+ .AsNoTracking()
346
+ .FirstOrDefaultAsync(p => p.Id == id, ct);
347
+ }
348
+
349
+ public async Task<IReadOnlyList<Product>> SearchAsync(
350
+ ProductSearchCriteria criteria,
351
+ CancellationToken ct = default)
352
+ {
353
+ var query = _context.Products.AsNoTracking();
354
+
355
+ if (!string.IsNullOrWhiteSpace(criteria.SearchTerm))
356
+ query = query.Where(p => EF.Functions.Like(p.Name, $"%{criteria.SearchTerm}%"));
357
+
358
+ if (criteria.CategoryId.HasValue)
359
+ query = query.Where(p => p.CategoryId == criteria.CategoryId);
360
+
361
+ if (criteria.MinPrice.HasValue)
362
+ query = query.Where(p => p.Price >= criteria.MinPrice);
363
+
364
+ if (criteria.MaxPrice.HasValue)
365
+ query = query.Where(p => p.Price <= criteria.MaxPrice);
366
+
367
+ return await query
368
+ .OrderBy(p => p.Name)
369
+ .Skip((criteria.Page - 1) * criteria.PageSize)
370
+ .Take(criteria.PageSize)
371
+ .ToListAsync(ct);
372
+ }
373
+ }
374
+ ```
375
+
376
+ ### Dapper for Performance
377
+
378
+ ```csharp
379
+ public class DapperProductRepository : IProductRepository
380
+ {
381
+ private readonly IDbConnection _connection;
382
+
383
+ public async Task<Product?> GetByIdAsync(string id, CancellationToken ct = default)
384
+ {
385
+ const string sql = """
386
+ SELECT Id, Name, Sku, Price, CategoryId, Stock, CreatedAt
387
+ FROM Products
388
+ WHERE Id = @Id AND IsDeleted = 0
389
+ """;
390
+
391
+ return await _connection.QueryFirstOrDefaultAsync<Product>(
392
+ new CommandDefinition(sql, new { Id = id }, cancellationToken: ct));
393
+ }
394
+
395
+ public async Task<IReadOnlyList<Product>> SearchAsync(
396
+ ProductSearchCriteria criteria,
397
+ CancellationToken ct = default)
398
+ {
399
+ var sql = new StringBuilder("""
400
+ SELECT Id, Name, Sku, Price, CategoryId, Stock, CreatedAt
401
+ FROM Products
402
+ WHERE IsDeleted = 0
403
+ """);
404
+
405
+ var parameters = new DynamicParameters();
406
+
407
+ if (!string.IsNullOrWhiteSpace(criteria.SearchTerm))
408
+ {
409
+ sql.Append(" AND Name LIKE @SearchTerm");
410
+ parameters.Add("SearchTerm", $"%{criteria.SearchTerm}%");
411
+ }
412
+
413
+ if (criteria.CategoryId.HasValue)
414
+ {
415
+ sql.Append(" AND CategoryId = @CategoryId");
416
+ parameters.Add("CategoryId", criteria.CategoryId);
417
+ }
418
+
419
+ if (criteria.MinPrice.HasValue)
420
+ {
421
+ sql.Append(" AND Price >= @MinPrice");
422
+ parameters.Add("MinPrice", criteria.MinPrice);
423
+ }
424
+
425
+ if (criteria.MaxPrice.HasValue)
426
+ {
427
+ sql.Append(" AND Price <= @MaxPrice");
428
+ parameters.Add("MaxPrice", criteria.MaxPrice);
429
+ }
430
+
431
+ sql.Append(" ORDER BY Name OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY");
432
+ parameters.Add("Offset", (criteria.Page - 1) * criteria.PageSize);
433
+ parameters.Add("PageSize", criteria.PageSize);
434
+
435
+ var results = await _connection.QueryAsync<Product>(
436
+ new CommandDefinition(sql.ToString(), parameters, cancellationToken: ct));
437
+
438
+ return results.ToList();
439
+ }
440
+
441
+ // Multi-mapping for related data
442
+ public async Task<Order?> GetOrderWithItemsAsync(int orderId, CancellationToken ct = default)
443
+ {
444
+ const string sql = """
445
+ SELECT o.*, oi.*, p.*
446
+ FROM Orders o
447
+ LEFT JOIN OrderItems oi ON o.Id = oi.OrderId
448
+ LEFT JOIN Products p ON oi.ProductId = p.Id
449
+ WHERE o.Id = @OrderId
450
+ """;
451
+
452
+ var orderDictionary = new Dictionary<int, Order>();
453
+
454
+ await _connection.QueryAsync<Order, OrderItem, Product, Order>(
455
+ new CommandDefinition(sql, new { OrderId = orderId }, cancellationToken: ct),
456
+ (order, item, product) =>
457
+ {
458
+ if (!orderDictionary.TryGetValue(order.Id, out var existingOrder))
459
+ {
460
+ existingOrder = order;
461
+ existingOrder.Items = new List<OrderItem>();
462
+ orderDictionary.Add(order.Id, existingOrder);
463
+ }
464
+
465
+ if (item != null)
466
+ {
467
+ item.Product = product;
468
+ existingOrder.Items.Add(item);
469
+ }
470
+
471
+ return existingOrder;
472
+ },
473
+ splitOn: "Id,Id");
474
+
475
+ return orderDictionary.Values.FirstOrDefault();
476
+ }
477
+ }
478
+ ```
479
+
480
+ ## Caching Patterns
481
+
482
+ ### Multi-Level Cache with Redis
483
+
484
+ ```csharp
485
+ public class CachedProductService : IProductService
486
+ {
487
+ private readonly IProductRepository _repository;
488
+ private readonly IMemoryCache _memoryCache;
489
+ private readonly IDistributedCache _distributedCache;
490
+ private readonly ILogger<CachedProductService> _logger;
491
+
492
+ private static readonly TimeSpan MemoryCacheDuration = TimeSpan.FromMinutes(1);
493
+ private static readonly TimeSpan DistributedCacheDuration = TimeSpan.FromMinutes(15);
494
+
495
+ public async Task<Product?> GetByIdAsync(string id, CancellationToken ct = default)
496
+ {
497
+ var cacheKey = $"product:{id}";
498
+
499
+ // L1: Memory cache (in-process, fastest)
500
+ if (_memoryCache.TryGetValue(cacheKey, out Product? cached))
501
+ {
502
+ _logger.LogDebug("L1 cache hit for {CacheKey}", cacheKey);
503
+ return cached;
504
+ }
505
+
506
+ // L2: Distributed cache (Redis)
507
+ var distributed = await _distributedCache.GetStringAsync(cacheKey, ct);
508
+ if (distributed != null)
509
+ {
510
+ _logger.LogDebug("L2 cache hit for {CacheKey}", cacheKey);
511
+ var product = JsonSerializer.Deserialize<Product>(distributed);
512
+
513
+ // Populate L1
514
+ _memoryCache.Set(cacheKey, product, MemoryCacheDuration);
515
+ return product;
516
+ }
517
+
518
+ // L3: Database
519
+ _logger.LogDebug("Cache miss for {CacheKey}, fetching from database", cacheKey);
520
+ var fromDb = await _repository.GetByIdAsync(id, ct);
521
+
522
+ if (fromDb != null)
523
+ {
524
+ var serialized = JsonSerializer.Serialize(fromDb);
525
+
526
+ // Populate both caches
527
+ await _distributedCache.SetStringAsync(
528
+ cacheKey,
529
+ serialized,
530
+ new DistributedCacheEntryOptions
531
+ {
532
+ AbsoluteExpirationRelativeToNow = DistributedCacheDuration
533
+ },
534
+ ct);
535
+
536
+ _memoryCache.Set(cacheKey, fromDb, MemoryCacheDuration);
537
+ }
538
+
539
+ return fromDb;
540
+ }
541
+
542
+ public async Task InvalidateAsync(string id, CancellationToken ct = default)
543
+ {
544
+ var cacheKey = $"product:{id}";
545
+
546
+ _memoryCache.Remove(cacheKey);
547
+ await _distributedCache.RemoveAsync(cacheKey, ct);
548
+
549
+ _logger.LogInformation("Invalidated cache for {CacheKey}", cacheKey);
550
+ }
551
+ }
552
+
553
+ // Stale-while-revalidate pattern
554
+ public class StaleWhileRevalidateCache<T>
555
+ {
556
+ private readonly IDistributedCache _cache;
557
+ private readonly TimeSpan _freshDuration;
558
+ private readonly TimeSpan _staleDuration;
559
+
560
+ public async Task<T?> GetOrCreateAsync(
561
+ string key,
562
+ Func<CancellationToken, Task<T>> factory,
563
+ CancellationToken ct = default)
564
+ {
565
+ var cached = await _cache.GetStringAsync(key, ct);
566
+
567
+ if (cached != null)
568
+ {
569
+ var entry = JsonSerializer.Deserialize<CacheEntry<T>>(cached)!;
570
+
571
+ if (entry.IsStale && !entry.IsExpired)
572
+ {
573
+ // Return stale data immediately, refresh in background
574
+ _ = Task.Run(async () =>
575
+ {
576
+ var fresh = await factory(CancellationToken.None);
577
+ await SetAsync(key, fresh, CancellationToken.None);
578
+ });
579
+ }
580
+
581
+ if (!entry.IsExpired)
582
+ return entry.Value;
583
+ }
584
+
585
+ // Cache miss or expired
586
+ var value = await factory(ct);
587
+ await SetAsync(key, value, ct);
588
+ return value;
589
+ }
590
+
591
+ private record CacheEntry<TValue>(TValue Value, DateTime CreatedAt)
592
+ {
593
+ public bool IsStale => DateTime.UtcNow - CreatedAt > _freshDuration;
594
+ public bool IsExpired => DateTime.UtcNow - CreatedAt > _staleDuration;
595
+ }
596
+ }
597
+ ```
598
+
599
+ ## Testing Patterns
600
+
601
+ ### Unit Tests with xUnit and Moq
602
+
603
+ ```csharp
604
+ public class OrderServiceTests
605
+ {
606
+ private readonly Mock<IOrderRepository> _mockRepository;
607
+ private readonly Mock<IStockService> _mockStockService;
608
+ private readonly Mock<IValidator<CreateOrderRequest>> _mockValidator;
609
+ private readonly OrderService _sut; // System Under Test
610
+
611
+ public OrderServiceTests()
612
+ {
613
+ _mockRepository = new Mock<IOrderRepository>();
614
+ _mockStockService = new Mock<IStockService>();
615
+ _mockValidator = new Mock<IValidator<CreateOrderRequest>>();
616
+
617
+ // Default: validation passes
618
+ _mockValidator
619
+ .Setup(v => v.ValidateAsync(It.IsAny<CreateOrderRequest>(), It.IsAny<CancellationToken>()))
620
+ .ReturnsAsync(new ValidationResult());
621
+
622
+ _sut = new OrderService(
623
+ _mockRepository.Object,
624
+ _mockStockService.Object,
625
+ _mockValidator.Object);
626
+ }
627
+
628
+ [Fact]
629
+ public async Task CreateOrderAsync_WithValidRequest_ReturnsSuccess()
630
+ {
631
+ // Arrange
632
+ var request = new CreateOrderRequest
633
+ {
634
+ ProductId = "PROD-001",
635
+ Quantity = 5,
636
+ CustomerOrderCode = "ORD-2024-001"
637
+ };
638
+
639
+ _mockStockService
640
+ .Setup(s => s.CheckAsync("PROD-001", 5, It.IsAny<CancellationToken>()))
641
+ .ReturnsAsync(new StockResult { IsAvailable = true, Available = 10 });
642
+
643
+ _mockRepository
644
+ .Setup(r => r.CreateAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()))
645
+ .ReturnsAsync(new Order { Id = 1, CustomerOrderCode = "ORD-2024-001" });
646
+
647
+ // Act
648
+ var result = await _sut.CreateOrderAsync(request);
649
+
650
+ // Assert
651
+ Assert.True(result.IsSuccess);
652
+ Assert.NotNull(result.Value);
653
+ Assert.Equal(1, result.Value.Id);
654
+
655
+ _mockRepository.Verify(
656
+ r => r.CreateAsync(It.Is<Order>(o => o.CustomerOrderCode == "ORD-2024-001"),
657
+ It.IsAny<CancellationToken>()),
658
+ Times.Once);
659
+ }
660
+
661
+ [Fact]
662
+ public async Task CreateOrderAsync_WithInsufficientStock_ReturnsFailure()
663
+ {
664
+ // Arrange
665
+ var request = new CreateOrderRequest { ProductId = "PROD-001", Quantity = 100 };
666
+
667
+ _mockStockService
668
+ .Setup(s => s.CheckAsync(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
669
+ .ReturnsAsync(new StockResult { IsAvailable = false, Available = 5 });
670
+
671
+ // Act
672
+ var result = await _sut.CreateOrderAsync(request);
673
+
674
+ // Assert
675
+ Assert.False(result.IsSuccess);
676
+ Assert.Equal("INSUFFICIENT_STOCK", result.ErrorCode);
677
+ Assert.Contains("5 available", result.Error);
678
+
679
+ _mockRepository.Verify(
680
+ r => r.CreateAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()),
681
+ Times.Never);
682
+ }
683
+
684
+ [Theory]
685
+ [InlineData(0)]
686
+ [InlineData(-1)]
687
+ [InlineData(-100)]
688
+ public async Task CreateOrderAsync_WithInvalidQuantity_ReturnsValidationError(int quantity)
689
+ {
690
+ // Arrange
691
+ var request = new CreateOrderRequest { ProductId = "PROD-001", Quantity = quantity };
692
+
693
+ _mockValidator
694
+ .Setup(v => v.ValidateAsync(request, It.IsAny<CancellationToken>()))
695
+ .ReturnsAsync(new ValidationResult(new[]
696
+ {
697
+ new ValidationFailure("Quantity", "Quantity must be greater than 0")
698
+ }));
699
+
700
+ // Act
701
+ var result = await _sut.CreateOrderAsync(request);
702
+
703
+ // Assert
704
+ Assert.False(result.IsSuccess);
705
+ Assert.Equal("VALIDATION_ERROR", result.ErrorCode);
706
+ }
707
+ }
708
+ ```
709
+
710
+ ### Integration Tests with WebApplicationFactory
711
+
712
+ ```csharp
713
+ public class ProductsApiTests : IClassFixture<WebApplicationFactory<Program>>
714
+ {
715
+ private readonly WebApplicationFactory<Program> _factory;
716
+ private readonly HttpClient _client;
717
+
718
+ public ProductsApiTests(WebApplicationFactory<Program> factory)
719
+ {
720
+ _factory = factory.WithWebHostBuilder(builder =>
721
+ {
722
+ builder.ConfigureServices(services =>
723
+ {
724
+ // Replace real database with in-memory
725
+ services.RemoveAll<DbContextOptions<AppDbContext>>();
726
+ services.AddDbContext<AppDbContext>(options =>
727
+ options.UseInMemoryDatabase("TestDb"));
728
+
729
+ // Replace Redis with memory cache
730
+ services.RemoveAll<IDistributedCache>();
731
+ services.AddDistributedMemoryCache();
732
+ });
733
+ });
734
+
735
+ _client = _factory.CreateClient();
736
+ }
737
+
738
+ [Fact]
739
+ public async Task GetProduct_WithValidId_ReturnsProduct()
740
+ {
741
+ // Arrange
742
+ using var scope = _factory.Services.CreateScope();
743
+ var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
744
+
745
+ context.Products.Add(new Product
746
+ {
747
+ Id = "TEST-001",
748
+ Name = "Test Product",
749
+ Price = 99.99m
750
+ });
751
+ await context.SaveChangesAsync();
752
+
753
+ // Act
754
+ var response = await _client.GetAsync("/api/products/TEST-001");
755
+
756
+ // Assert
757
+ response.EnsureSuccessStatusCode();
758
+ var product = await response.Content.ReadFromJsonAsync<Product>();
759
+ Assert.Equal("Test Product", product!.Name);
760
+ }
761
+
762
+ [Fact]
763
+ public async Task GetProduct_WithInvalidId_Returns404()
764
+ {
765
+ // Act
766
+ var response = await _client.GetAsync("/api/products/NONEXISTENT");
767
+
768
+ // Assert
769
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
770
+ }
771
+ }
772
+ ```
773
+
774
+ ## Best Practices
775
+
776
+ ### DO
777
+ 1. **Use async/await** all the way through the call stack
778
+ 2. **Inject dependencies** through constructor injection
779
+ 3. **Use IOptions<T>** for typed configuration
780
+ 4. **Return Result types** instead of throwing exceptions for business logic
781
+ 5. **Use CancellationToken** in all async methods
782
+ 6. **Prefer Dapper** for read-heavy, performance-critical queries
783
+ 7. **Use EF Core** for complex domain models with change tracking
784
+ 8. **Cache aggressively** with proper invalidation strategies
785
+ 9. **Write unit tests** for business logic, integration tests for APIs
786
+ 10. **Use record types** for DTOs and immutable data
787
+
788
+ ### DON'T
789
+ 1. **Don't block on async** with `.Result` or `.Wait()`
790
+ 2. **Don't use async void** except for event handlers
791
+ 3. **Don't catch generic Exception** without re-throwing or logging
792
+ 4. **Don't hardcode** configuration values
793
+ 5. **Don't expose EF entities** directly in APIs (use DTOs)
794
+ 6. **Don't forget** `AsNoTracking()` for read-only queries
795
+ 7. **Don't ignore** CancellationToken parameters
796
+ 8. **Don't create** `new HttpClient()` manually (use IHttpClientFactory)
797
+ 9. **Don't mix** sync and async code unnecessarily
798
+ 10. **Don't skip** validation at API boundaries
799
+
800
+ ## Common Pitfalls
801
+
802
+ - **N+1 Queries**: Use `.Include()` or explicit joins
803
+ - **Memory Leaks**: Dispose IDisposable resources, use `using`
804
+ - **Deadlocks**: Don't mix sync and async, use ConfigureAwait(false) in libraries
805
+ - **Over-fetching**: Select only needed columns, use projections
806
+ - **Missing Indexes**: Check query plans, add indexes for common filters
807
+ - **Timeout Issues**: Configure appropriate timeouts for HTTP clients
808
+ - **Cache Stampede**: Use distributed locks for cache population
809
+
810
+ ## Resources
811
+
812
+ - **assets/service-template.cs**: Complete service implementation template
813
+ - **assets/repository-template.cs**: Repository pattern implementation
814
+ - **references/ef-core-best-practices.md**: EF Core optimization guide
815
+ - **references/dapper-patterns.md**: Advanced Dapper usage patterns