claudient 0.1.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.
Files changed (283) hide show
  1. package/.claude-plugin/plugin.json +42 -0
  2. package/CONTEXT.md +58 -0
  3. package/README.md +165 -0
  4. package/agents/build-resolvers/de/python-resolver.md +64 -0
  5. package/agents/build-resolvers/de/typescript-resolver.md +65 -0
  6. package/agents/build-resolvers/es/python-resolver.md +64 -0
  7. package/agents/build-resolvers/es/typescript-resolver.md +65 -0
  8. package/agents/build-resolvers/fr/python-resolver.md +64 -0
  9. package/agents/build-resolvers/fr/typescript-resolver.md +65 -0
  10. package/agents/build-resolvers/nl/python-resolver.md +64 -0
  11. package/agents/build-resolvers/nl/typescript-resolver.md +65 -0
  12. package/agents/build-resolvers/python-resolver.md +62 -0
  13. package/agents/build-resolvers/typescript-resolver.md +63 -0
  14. package/agents/core/architect.md +64 -0
  15. package/agents/core/code-reviewer.md +78 -0
  16. package/agents/core/de/architect.md +66 -0
  17. package/agents/core/de/code-reviewer.md +80 -0
  18. package/agents/core/de/planner.md +63 -0
  19. package/agents/core/de/security-reviewer.md +93 -0
  20. package/agents/core/es/architect.md +66 -0
  21. package/agents/core/es/code-reviewer.md +80 -0
  22. package/agents/core/es/planner.md +63 -0
  23. package/agents/core/es/security-reviewer.md +93 -0
  24. package/agents/core/fr/architect.md +66 -0
  25. package/agents/core/fr/code-reviewer.md +80 -0
  26. package/agents/core/fr/planner.md +63 -0
  27. package/agents/core/fr/security-reviewer.md +93 -0
  28. package/agents/core/nl/architect.md +66 -0
  29. package/agents/core/nl/code-reviewer.md +80 -0
  30. package/agents/core/nl/planner.md +63 -0
  31. package/agents/core/nl/security-reviewer.md +93 -0
  32. package/agents/core/planner.md +61 -0
  33. package/agents/core/security-reviewer.md +91 -0
  34. package/guides/agent-orchestration.md +231 -0
  35. package/guides/de/agent-orchestration.md +174 -0
  36. package/guides/de/getting-started.md +164 -0
  37. package/guides/de/hooks-cookbook.md +160 -0
  38. package/guides/de/memory-management.md +153 -0
  39. package/guides/de/security.md +180 -0
  40. package/guides/de/skill-authoring.md +214 -0
  41. package/guides/de/token-optimization.md +156 -0
  42. package/guides/es/agent-orchestration.md +174 -0
  43. package/guides/es/getting-started.md +164 -0
  44. package/guides/es/hooks-cookbook.md +160 -0
  45. package/guides/es/memory-management.md +153 -0
  46. package/guides/es/security.md +180 -0
  47. package/guides/es/skill-authoring.md +214 -0
  48. package/guides/es/token-optimization.md +156 -0
  49. package/guides/fr/agent-orchestration.md +174 -0
  50. package/guides/fr/getting-started.md +164 -0
  51. package/guides/fr/hooks-cookbook.md +227 -0
  52. package/guides/fr/memory-management.md +169 -0
  53. package/guides/fr/security.md +180 -0
  54. package/guides/fr/skill-authoring.md +214 -0
  55. package/guides/fr/token-optimization.md +158 -0
  56. package/guides/getting-started.md +164 -0
  57. package/guides/hooks-cookbook.md +423 -0
  58. package/guides/memory-management.md +192 -0
  59. package/guides/nl/agent-orchestration.md +174 -0
  60. package/guides/nl/getting-started.md +164 -0
  61. package/guides/nl/hooks-cookbook.md +160 -0
  62. package/guides/nl/memory-management.md +153 -0
  63. package/guides/nl/security.md +180 -0
  64. package/guides/nl/skill-authoring.md +214 -0
  65. package/guides/nl/token-optimization.md +156 -0
  66. package/guides/security.md +229 -0
  67. package/guides/skill-authoring.md +226 -0
  68. package/guides/token-optimization.md +169 -0
  69. package/hooks/lifecycle/cost-tracker.md +49 -0
  70. package/hooks/lifecycle/cost-tracker.sh +59 -0
  71. package/hooks/lifecycle/pre-compact-save.md +56 -0
  72. package/hooks/lifecycle/pre-compact-save.sh +37 -0
  73. package/hooks/lifecycle/session-start.md +50 -0
  74. package/hooks/lifecycle/session-start.sh +47 -0
  75. package/hooks/post-tool-use/audit-log.md +53 -0
  76. package/hooks/post-tool-use/audit-log.sh +53 -0
  77. package/hooks/post-tool-use/prettier.md +53 -0
  78. package/hooks/post-tool-use/prettier.sh +49 -0
  79. package/hooks/pre-tool-use/block-dangerous.md +48 -0
  80. package/hooks/pre-tool-use/block-dangerous.sh +76 -0
  81. package/hooks/pre-tool-use/git-push-confirm.md +46 -0
  82. package/hooks/pre-tool-use/git-push-confirm.sh +36 -0
  83. package/mcp/configs/github.json +11 -0
  84. package/mcp/configs/postgres.json +11 -0
  85. package/mcp/de/recommended-servers.md +170 -0
  86. package/mcp/es/recommended-servers.md +170 -0
  87. package/mcp/fr/recommended-servers.md +170 -0
  88. package/mcp/nl/recommended-servers.md +170 -0
  89. package/mcp/recommended-servers.md +168 -0
  90. package/package.json +45 -0
  91. package/prompts/project-starters/de/fastapi-project.md +62 -0
  92. package/prompts/project-starters/de/nextjs-project.md +82 -0
  93. package/prompts/project-starters/es/fastapi-project.md +62 -0
  94. package/prompts/project-starters/es/nextjs-project.md +82 -0
  95. package/prompts/project-starters/fastapi-project.md +60 -0
  96. package/prompts/project-starters/fr/fastapi-project.md +62 -0
  97. package/prompts/project-starters/fr/nextjs-project.md +82 -0
  98. package/prompts/project-starters/nextjs-project.md +80 -0
  99. package/prompts/project-starters/nl/fastapi-project.md +62 -0
  100. package/prompts/project-starters/nl/nextjs-project.md +82 -0
  101. package/prompts/system-prompts/ai-product.md +80 -0
  102. package/prompts/system-prompts/data-pipeline.md +76 -0
  103. package/prompts/system-prompts/de/ai-product.md +82 -0
  104. package/prompts/system-prompts/de/data-pipeline.md +78 -0
  105. package/prompts/system-prompts/de/saas-backend.md +71 -0
  106. package/prompts/system-prompts/es/ai-product.md +82 -0
  107. package/prompts/system-prompts/es/data-pipeline.md +78 -0
  108. package/prompts/system-prompts/es/saas-backend.md +71 -0
  109. package/prompts/system-prompts/fr/ai-product.md +82 -0
  110. package/prompts/system-prompts/fr/data-pipeline.md +78 -0
  111. package/prompts/system-prompts/fr/saas-backend.md +71 -0
  112. package/prompts/system-prompts/nl/ai-product.md +82 -0
  113. package/prompts/system-prompts/nl/data-pipeline.md +78 -0
  114. package/prompts/system-prompts/nl/saas-backend.md +71 -0
  115. package/prompts/system-prompts/saas-backend.md +69 -0
  116. package/prompts/task-specific/changelog.md +81 -0
  117. package/prompts/task-specific/de/changelog.md +83 -0
  118. package/prompts/task-specific/de/debugging.md +78 -0
  119. package/prompts/task-specific/de/pr-description.md +69 -0
  120. package/prompts/task-specific/debugging.md +76 -0
  121. package/prompts/task-specific/es/changelog.md +83 -0
  122. package/prompts/task-specific/es/debugging.md +78 -0
  123. package/prompts/task-specific/es/pr-description.md +69 -0
  124. package/prompts/task-specific/fr/changelog.md +83 -0
  125. package/prompts/task-specific/fr/debugging.md +78 -0
  126. package/prompts/task-specific/fr/pr-description.md +69 -0
  127. package/prompts/task-specific/nl/changelog.md +83 -0
  128. package/prompts/task-specific/nl/debugging.md +78 -0
  129. package/prompts/task-specific/nl/pr-description.md +69 -0
  130. package/prompts/task-specific/pr-description.md +67 -0
  131. package/rules/common/coding-style.md +45 -0
  132. package/rules/common/de/coding-style.md +47 -0
  133. package/rules/common/de/git.md +48 -0
  134. package/rules/common/de/performance.md +40 -0
  135. package/rules/common/de/security.md +45 -0
  136. package/rules/common/de/testing.md +45 -0
  137. package/rules/common/es/coding-style.md +47 -0
  138. package/rules/common/es/git.md +48 -0
  139. package/rules/common/es/performance.md +40 -0
  140. package/rules/common/es/security.md +45 -0
  141. package/rules/common/es/testing.md +45 -0
  142. package/rules/common/fr/coding-style.md +47 -0
  143. package/rules/common/fr/git.md +48 -0
  144. package/rules/common/fr/performance.md +40 -0
  145. package/rules/common/fr/security.md +45 -0
  146. package/rules/common/fr/testing.md +45 -0
  147. package/rules/common/git.md +46 -0
  148. package/rules/common/nl/coding-style.md +47 -0
  149. package/rules/common/nl/git.md +48 -0
  150. package/rules/common/nl/performance.md +40 -0
  151. package/rules/common/nl/security.md +45 -0
  152. package/rules/common/nl/testing.md +45 -0
  153. package/rules/common/performance.md +38 -0
  154. package/rules/common/security.md +43 -0
  155. package/rules/common/testing.md +43 -0
  156. package/rules/language-specific/de/go.md +48 -0
  157. package/rules/language-specific/de/python.md +38 -0
  158. package/rules/language-specific/de/typescript.md +51 -0
  159. package/rules/language-specific/es/go.md +48 -0
  160. package/rules/language-specific/es/python.md +38 -0
  161. package/rules/language-specific/es/typescript.md +51 -0
  162. package/rules/language-specific/fr/go.md +48 -0
  163. package/rules/language-specific/fr/python.md +38 -0
  164. package/rules/language-specific/fr/typescript.md +51 -0
  165. package/rules/language-specific/go.md +46 -0
  166. package/rules/language-specific/nl/go.md +48 -0
  167. package/rules/language-specific/nl/python.md +38 -0
  168. package/rules/language-specific/nl/typescript.md +51 -0
  169. package/rules/language-specific/python.md +36 -0
  170. package/rules/language-specific/typescript.md +49 -0
  171. package/scripts/cli.js +161 -0
  172. package/scripts/link-skills.sh +35 -0
  173. package/scripts/list-skills.sh +34 -0
  174. package/skills/ai-engineering/agent-construction.md +285 -0
  175. package/skills/ai-engineering/claude-api.md +248 -0
  176. package/skills/ai-engineering/de/agent-construction.md +287 -0
  177. package/skills/ai-engineering/de/claude-api.md +250 -0
  178. package/skills/ai-engineering/es/agent-construction.md +287 -0
  179. package/skills/ai-engineering/es/claude-api.md +250 -0
  180. package/skills/ai-engineering/fr/agent-construction.md +287 -0
  181. package/skills/ai-engineering/fr/claude-api.md +250 -0
  182. package/skills/ai-engineering/nl/agent-construction.md +287 -0
  183. package/skills/ai-engineering/nl/claude-api.md +250 -0
  184. package/skills/backend/dotnet/csharp.md +304 -0
  185. package/skills/backend/dotnet/de/csharp.md +306 -0
  186. package/skills/backend/dotnet/es/csharp.md +306 -0
  187. package/skills/backend/dotnet/fr/csharp.md +306 -0
  188. package/skills/backend/dotnet/nl/csharp.md +306 -0
  189. package/skills/backend/go/de/go.md +307 -0
  190. package/skills/backend/go/es/go.md +307 -0
  191. package/skills/backend/go/fr/go.md +307 -0
  192. package/skills/backend/go/go.md +305 -0
  193. package/skills/backend/go/nl/go.md +307 -0
  194. package/skills/backend/nodejs/de/nestjs.md +274 -0
  195. package/skills/backend/nodejs/de/nextjs.md +222 -0
  196. package/skills/backend/nodejs/es/nestjs.md +274 -0
  197. package/skills/backend/nodejs/es/nextjs.md +222 -0
  198. package/skills/backend/nodejs/fr/nestjs.md +274 -0
  199. package/skills/backend/nodejs/fr/nextjs.md +222 -0
  200. package/skills/backend/nodejs/nestjs.md +272 -0
  201. package/skills/backend/nodejs/nextjs.md +220 -0
  202. package/skills/backend/nodejs/nl/nestjs.md +274 -0
  203. package/skills/backend/nodejs/nl/nextjs.md +222 -0
  204. package/skills/backend/python/de/django.md +285 -0
  205. package/skills/backend/python/de/fastapi.md +244 -0
  206. package/skills/backend/python/django.md +283 -0
  207. package/skills/backend/python/es/django.md +285 -0
  208. package/skills/backend/python/es/fastapi.md +244 -0
  209. package/skills/backend/python/fastapi.md +242 -0
  210. package/skills/backend/python/fr/django.md +285 -0
  211. package/skills/backend/python/fr/fastapi.md +244 -0
  212. package/skills/backend/python/nl/django.md +285 -0
  213. package/skills/backend/python/nl/fastapi.md +244 -0
  214. package/skills/data-ml/dbt-data-pipelines.md +155 -0
  215. package/skills/data-ml/de/dbt-data-pipelines.md +157 -0
  216. package/skills/data-ml/de/pandas-polars.md +147 -0
  217. package/skills/data-ml/de/pytorch-tensorflow.md +171 -0
  218. package/skills/data-ml/es/dbt-data-pipelines.md +157 -0
  219. package/skills/data-ml/es/pandas-polars.md +147 -0
  220. package/skills/data-ml/es/pytorch-tensorflow.md +171 -0
  221. package/skills/data-ml/fr/dbt-data-pipelines.md +157 -0
  222. package/skills/data-ml/fr/pandas-polars.md +147 -0
  223. package/skills/data-ml/fr/pytorch-tensorflow.md +171 -0
  224. package/skills/data-ml/nl/dbt-data-pipelines.md +157 -0
  225. package/skills/data-ml/nl/pandas-polars.md +147 -0
  226. package/skills/data-ml/nl/pytorch-tensorflow.md +171 -0
  227. package/skills/data-ml/pandas-polars.md +145 -0
  228. package/skills/data-ml/pytorch-tensorflow.md +169 -0
  229. package/skills/database/de/graphql.md +181 -0
  230. package/skills/database/es/graphql.md +181 -0
  231. package/skills/database/fr/graphql.md +181 -0
  232. package/skills/database/graphql.md +179 -0
  233. package/skills/database/nl/graphql.md +181 -0
  234. package/skills/devops-infra/de/docker.md +133 -0
  235. package/skills/devops-infra/de/github-actions.md +179 -0
  236. package/skills/devops-infra/de/kubernetes.md +129 -0
  237. package/skills/devops-infra/de/terraform.md +130 -0
  238. package/skills/devops-infra/docker.md +131 -0
  239. package/skills/devops-infra/es/docker.md +133 -0
  240. package/skills/devops-infra/es/github-actions.md +179 -0
  241. package/skills/devops-infra/es/kubernetes.md +129 -0
  242. package/skills/devops-infra/es/terraform.md +130 -0
  243. package/skills/devops-infra/fr/docker.md +133 -0
  244. package/skills/devops-infra/fr/github-actions.md +179 -0
  245. package/skills/devops-infra/fr/kubernetes.md +129 -0
  246. package/skills/devops-infra/fr/terraform.md +130 -0
  247. package/skills/devops-infra/github-actions.md +177 -0
  248. package/skills/devops-infra/kubernetes.md +127 -0
  249. package/skills/devops-infra/nl/docker.md +133 -0
  250. package/skills/devops-infra/nl/github-actions.md +179 -0
  251. package/skills/devops-infra/nl/kubernetes.md +129 -0
  252. package/skills/devops-infra/nl/terraform.md +130 -0
  253. package/skills/devops-infra/terraform.md +128 -0
  254. package/skills/finance-payments/de/stripe.md +187 -0
  255. package/skills/finance-payments/es/stripe.md +187 -0
  256. package/skills/finance-payments/fr/stripe.md +187 -0
  257. package/skills/finance-payments/nl/stripe.md +187 -0
  258. package/skills/finance-payments/stripe.md +185 -0
  259. package/workflows/code-review.md +151 -0
  260. package/workflows/de/code-review.md +153 -0
  261. package/workflows/de/debugging-session.md +146 -0
  262. package/workflows/de/feature-development.md +155 -0
  263. package/workflows/de/new-project-bootstrap.md +175 -0
  264. package/workflows/de/refactor-safely.md +150 -0
  265. package/workflows/debugging-session.md +144 -0
  266. package/workflows/es/code-review.md +153 -0
  267. package/workflows/es/debugging-session.md +146 -0
  268. package/workflows/es/feature-development.md +155 -0
  269. package/workflows/es/new-project-bootstrap.md +175 -0
  270. package/workflows/es/refactor-safely.md +150 -0
  271. package/workflows/feature-development.md +153 -0
  272. package/workflows/fr/code-review.md +153 -0
  273. package/workflows/fr/debugging-session.md +146 -0
  274. package/workflows/fr/feature-development.md +155 -0
  275. package/workflows/fr/new-project-bootstrap.md +175 -0
  276. package/workflows/fr/refactor-safely.md +150 -0
  277. package/workflows/new-project-bootstrap.md +173 -0
  278. package/workflows/nl/code-review.md +153 -0
  279. package/workflows/nl/debugging-session.md +146 -0
  280. package/workflows/nl/feature-development.md +155 -0
  281. package/workflows/nl/new-project-bootstrap.md +175 -0
  282. package/workflows/nl/refactor-safely.md +150 -0
  283. package/workflows/refactor-safely.md +148 -0
@@ -0,0 +1,306 @@
1
+ > 🇫🇷 This is the French translation. [English version](../csharp.md).
2
+
3
+ # Compétence C#/.NET
4
+
5
+ ## Quand activer
6
+ - Construire une Web API .NET (minimal API ou basée sur des controllers)
7
+ - Configurer Entity Framework Core avec des migrations
8
+ - Configurer le conteneur d'injection de dépendances .NET
9
+ - Rédiger des services en arrière-plan avec `IHostedService` ou `BackgroundService`
10
+ - Implémenter des composants de pipeline middleware
11
+ - Rédiger des requêtes LINQ et comprendre l'exécution différée
12
+ - Configurer async/await correctement dans ASP.NET Core
13
+
14
+ ## Quand NE PAS utiliser
15
+ - Services Node.js ou Python
16
+ - Bases de code legacy .NET Framework (antérieures à .NET 5) — les patterns diffèrent
17
+ - Frontend Blazor ou MAUI — préoccupations différentes
18
+ - Développement de jeux Unity — runtime différent
19
+
20
+ ## Instructions
21
+
22
+ ### Structure du projet
23
+ ```
24
+ MyApi/
25
+ ├── MyApi.sln
26
+ ├── src/
27
+ │ └── MyApi/
28
+ │ ├── Program.cs # Point d'entrée + conteneur DI
29
+ │ ├── appsettings.json
30
+ │ ├── appsettings.Development.json
31
+ │ ├── Controllers/ # API basée sur des controllers
32
+ │ ├── Endpoints/ # Extensions minimal API
33
+ │ ├── Models/ # Entités EF Core
34
+ │ ├── DTOs/ # Formes de requête/réponse
35
+ │ ├── Services/ # Interfaces + implémentations de logique métier
36
+ │ ├── Data/
37
+ │ │ └── AppDbContext.cs
38
+ │ └── Middleware/
39
+ └── tests/
40
+ └── MyApi.Tests/
41
+ ```
42
+
43
+ ### Program.cs — configuration minimal API
44
+ ```csharp
45
+ // Program.cs — .NET 6+ instructions de niveau supérieur + minimal API
46
+ var builder = WebApplication.CreateBuilder(args);
47
+
48
+ // Enregistrer les services
49
+ builder.Services.AddDbContext<AppDbContext>(options =>
50
+ options.UseNpgsql(builder.Configuration.GetConnectionString("Default")));
51
+
52
+ builder.Services.AddScoped<IUserService, UserService>();
53
+ builder.Services.AddControllers();
54
+ builder.Services.AddEndpointsApiExplorer();
55
+ builder.Services.AddSwaggerGen();
56
+
57
+ var app = builder.Build();
58
+
59
+ if (app.Environment.IsDevelopment())
60
+ {
61
+ app.UseSwagger();
62
+ app.UseSwaggerUI();
63
+ }
64
+
65
+ app.UseHttpsRedirection();
66
+ app.UseAuthentication();
67
+ app.UseAuthorization();
68
+ app.MapControllers();
69
+
70
+ // Endpoints minimal API
71
+ app.MapGroup("/api/v1").MapUserEndpoints();
72
+
73
+ app.Run();
74
+ ```
75
+
76
+ ### Entité EF Core + DbContext
77
+ ```csharp
78
+ // Models/User.cs
79
+ public class User
80
+ {
81
+ public Guid Id { get; set; } = Guid.NewGuid();
82
+ public required string Email { get; set; }
83
+ public required string PasswordHash { get; set; }
84
+ public bool IsActive { get; set; } = true;
85
+ public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
86
+
87
+ public ICollection<Post> Posts { get; set; } = [];
88
+ }
89
+
90
+ // Data/AppDbContext.cs
91
+ public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
92
+ {
93
+ public DbSet<User> Users => Set<User>();
94
+ public DbSet<Post> Posts => Set<Post>();
95
+
96
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
97
+ {
98
+ modelBuilder.Entity<User>(e =>
99
+ {
100
+ e.HasIndex(u => u.Email).IsUnique();
101
+ e.Property(u => u.Email).HasMaxLength(320);
102
+ });
103
+ }
104
+ }
105
+ ```
106
+
107
+ ### Injection de dépendances — services
108
+ ```csharp
109
+ // Services/IUserService.cs
110
+ public interface IUserService
111
+ {
112
+ Task<UserDto> GetByIdAsync(Guid id, CancellationToken ct = default);
113
+ Task<UserDto> CreateAsync(CreateUserRequest request, CancellationToken ct = default);
114
+ }
115
+
116
+ // Services/UserService.cs
117
+ public class UserService(AppDbContext db) : IUserService
118
+ {
119
+ public async Task<UserDto> GetByIdAsync(Guid id, CancellationToken ct = default)
120
+ {
121
+ var user = await db.Users
122
+ .AsNoTracking()
123
+ .FirstOrDefaultAsync(u => u.Id == id, ct)
124
+ ?? throw new NotFoundException($"User {id} not found");
125
+
126
+ return new UserDto(user.Id, user.Email, user.CreatedAt);
127
+ }
128
+
129
+ public async Task<UserDto> CreateAsync(CreateUserRequest request, CancellationToken ct = default)
130
+ {
131
+ if (await db.Users.AnyAsync(u => u.Email == request.Email, ct))
132
+ throw new ConflictException("Email already in use");
133
+
134
+ var user = new User
135
+ {
136
+ Email = request.Email,
137
+ PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.Password)
138
+ };
139
+ db.Users.Add(user);
140
+ await db.SaveChangesAsync(ct);
141
+ return new UserDto(user.Id, user.Email, user.CreatedAt);
142
+ }
143
+ }
144
+ ```
145
+
146
+ ### API basée sur des controllers
147
+ ```csharp
148
+ [ApiController]
149
+ [Route("api/v1/[controller]")]
150
+ public class UsersController(IUserService userService) : ControllerBase
151
+ {
152
+ [HttpGet("{id:guid}")]
153
+ [ProducesResponseType<UserDto>(StatusCodes.Status200OK)]
154
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
155
+ public async Task<IActionResult> GetUser(Guid id, CancellationToken ct)
156
+ {
157
+ var user = await userService.GetByIdAsync(id, ct);
158
+ return Ok(user);
159
+ }
160
+
161
+ [HttpPost]
162
+ [ProducesResponseType<UserDto>(StatusCodes.Status201Created)]
163
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
164
+ [ProducesResponseType(StatusCodes.Status409Conflict)]
165
+ public async Task<IActionResult> CreateUser(
166
+ [FromBody] CreateUserRequest request,
167
+ CancellationToken ct)
168
+ {
169
+ var user = await userService.CreateAsync(request, ct);
170
+ return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
171
+ }
172
+ }
173
+ ```
174
+
175
+ ### Endpoints minimal API (pattern de méthode d'extension)
176
+ ```csharp
177
+ // Endpoints/UserEndpoints.cs
178
+ public static class UserEndpoints
179
+ {
180
+ public static RouteGroupBuilder MapUserEndpoints(this RouteGroupBuilder group)
181
+ {
182
+ var users = group.MapGroup("/users").WithTags("Users");
183
+
184
+ users.MapGet("/{id:guid}", async (Guid id, IUserService svc, CancellationToken ct) =>
185
+ {
186
+ var user = await svc.GetByIdAsync(id, ct);
187
+ return Results.Ok(user);
188
+ })
189
+ .WithName("GetUser")
190
+ .Produces<UserDto>();
191
+
192
+ return group;
193
+ }
194
+ }
195
+ ```
196
+
197
+ ### Middleware
198
+ ```csharp
199
+ // Middleware/RequestLoggingMiddleware.cs
200
+ public class RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
201
+ {
202
+ public async Task InvokeAsync(HttpContext context)
203
+ {
204
+ var sw = Stopwatch.StartNew();
205
+ try
206
+ {
207
+ await next(context);
208
+ }
209
+ finally
210
+ {
211
+ sw.Stop();
212
+ logger.LogInformation(
213
+ "{Method} {Path} {StatusCode} in {Elapsed}ms",
214
+ context.Request.Method,
215
+ context.Request.Path,
216
+ context.Response.StatusCode,
217
+ sw.ElapsedMilliseconds);
218
+ }
219
+ }
220
+ }
221
+
222
+ // Enregistrer dans Program.cs avant les autres middlewares :
223
+ app.UseMiddleware<RequestLoggingMiddleware>();
224
+ ```
225
+
226
+ ### Services en arrière-plan
227
+ ```csharp
228
+ // Services/CleanupService.cs
229
+ public class CleanupService(IServiceProvider services, ILogger<CleanupService> logger)
230
+ : BackgroundService
231
+ {
232
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
233
+ {
234
+ while (!stoppingToken.IsCancellationRequested)
235
+ {
236
+ await DoCleanupAsync(stoppingToken);
237
+ await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
238
+ }
239
+ }
240
+
241
+ private async Task DoCleanupAsync(CancellationToken ct)
242
+ {
243
+ // Utiliser un nouveau scope pour chaque itération — BackgroundService est singleton
244
+ using var scope = services.CreateScope();
245
+ var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
246
+ var cutoff = DateTimeOffset.UtcNow.AddDays(-30);
247
+ await db.Sessions.Where(s => s.ExpiresAt < cutoff).ExecuteDeleteAsync(ct);
248
+ }
249
+ }
250
+
251
+ // Enregistrer : builder.Services.AddHostedService<CleanupService>();
252
+ ```
253
+
254
+ ### Bonnes pratiques LINQ
255
+ ```csharp
256
+ // Toujours utiliser AsNoTracking() pour les requêtes en lecture seule
257
+ var users = await db.Users.AsNoTracking().Where(u => u.IsActive).ToListAsync(ct);
258
+
259
+ // Sélectionner uniquement les colonnes nécessaires — éviter de charger des entités complètes pour les projections
260
+ var emails = await db.Users
261
+ .Where(u => u.IsActive)
262
+ .Select(u => u.Email)
263
+ .ToListAsync(ct);
264
+
265
+ // Utiliser ExecuteUpdateAsync/ExecuteDeleteAsync pour les opérations en masse — évite de charger des entités
266
+ await db.Users
267
+ .Where(u => !u.IsActive && u.CreatedAt < cutoff)
268
+ .ExecuteDeleteAsync(ct);
269
+
270
+ // Éviter N+1 : utiliser Include() pour les données liées
271
+ var posts = await db.Posts
272
+ .Include(p => p.Author)
273
+ .Include(p => p.Tags)
274
+ .Where(p => p.Published)
275
+ .AsNoTracking()
276
+ .ToListAsync(ct);
277
+ ```
278
+
279
+ ### DTOs et records
280
+ ```csharp
281
+ // Utiliser des records pour les DTOs immuables
282
+ public record UserDto(Guid Id, string Email, DateTimeOffset CreatedAt);
283
+ public record CreateUserRequest(
284
+ [property: Required, EmailAddress] string Email,
285
+ [property: Required, MinLength(8)] string Password
286
+ );
287
+
288
+ // Types de réponse avec problem details (intégré dans .NET)
289
+ // Retourner Results.Problem() ou lever des exceptions catchées par le middleware
290
+ ```
291
+
292
+ ## Exemple
293
+
294
+ **Utilisateur :** Ajouter une ressource `BlogPost` à une Web API .NET : endpoints CRUD, entité EF Core, migrations et un job en arrière-plan qui publie les articles planifiés.
295
+
296
+ **Sortie attendue :**
297
+ - `Models/BlogPost.cs` — entité avec `Id`, `Title`, `Body`, `AuthorId` (FK vers User), `PublishedAt` (nullable), `ScheduledFor` (nullable)
298
+ - `DTOs/BlogPostDtos.cs` — record `BlogPostDto`, record `CreateBlogPostRequest` avec validation `[Required]`
299
+ - `Services/IBlogPostService.cs` + `BlogPostService.cs` — méthodes CRUD, `GetPendingScheduledAsync` pour le job en arrière-plan
300
+ - `Controllers/BlogPostsController.cs` — tous les CRUD avec codes de statut appropriés
301
+ - `Services/PostPublisherService.cs` — `BackgroundService` qui vérifie toutes les minutes et publie les articles dus
302
+ - Migration EF Core : `dotnet ef migrations add AddBlogPosts`
303
+
304
+ ---
305
+
306
+ > **Travaillez avec nous :** Claudient est soutenu par [Uitbreiden](https://uitbreiden.com/) — nous construisons des produits IA et des solutions B2B avec des communautés de développeurs. [uitbreiden.com](https://uitbreiden.com/)
@@ -0,0 +1,306 @@
1
+ > 🇳🇱 Dit is de Nederlandse vertaling. [Engelse versie](../csharp.md).
2
+
3
+ # C#/.NET Skill
4
+
5
+ ## Wanneer te activeren
6
+ - Een .NET Web API bouwen (minimale API of op controllers gebaseerd)
7
+ - Entity Framework Core instellen met migraties
8
+ - De .NET dependency injection-container configureren
9
+ - Achtergrondservices schrijven met `IHostedService` of `BackgroundService`
10
+ - Middleware-pipeline-componenten implementeren
11
+ - LINQ-queries schrijven en uitgestelde uitvoering begrijpen
12
+ - async/await correct instellen in ASP.NET Core
13
+
14
+ ## Wanneer NIET te gebruiken
15
+ - Node.js of Python-services
16
+ - .NET Framework (pre-.NET 5) legacy-codebases — patronen verschillen
17
+ - Blazor of MAUI frontend — andere concerns
18
+ - Unity-spelontwikkeling — andere runtime
19
+
20
+ ## Instructies
21
+
22
+ ### Projectstructuur
23
+ ```
24
+ MyApi/
25
+ ├── MyApi.sln
26
+ ├── src/
27
+ │ └── MyApi/
28
+ │ ├── Program.cs # Ingangspunt + DI-container
29
+ │ ├── appsettings.json
30
+ │ ├── appsettings.Development.json
31
+ │ ├── Controllers/ # Op controllers gebaseerde API
32
+ │ ├── Endpoints/ # Minimale API-extensies
33
+ │ ├── Models/ # EF Core-entiteiten
34
+ │ ├── DTOs/ # Aanvraag/respons-vormen
35
+ │ ├── Services/ # Bedrijfslogica-interfaces + -implementaties
36
+ │ ├── Data/
37
+ │ │ └── AppDbContext.cs
38
+ │ └── Middleware/
39
+ └── tests/
40
+ └── MyApi.Tests/
41
+ ```
42
+
43
+ ### Program.cs — minimale API-instelling
44
+ ```csharp
45
+ // Program.cs — .NET 6+ top-level statements + minimale API
46
+ var builder = WebApplication.CreateBuilder(args);
47
+
48
+ // Services registreren
49
+ builder.Services.AddDbContext<AppDbContext>(options =>
50
+ options.UseNpgsql(builder.Configuration.GetConnectionString("Default")));
51
+
52
+ builder.Services.AddScoped<IUserService, UserService>();
53
+ builder.Services.AddControllers();
54
+ builder.Services.AddEndpointsApiExplorer();
55
+ builder.Services.AddSwaggerGen();
56
+
57
+ var app = builder.Build();
58
+
59
+ if (app.Environment.IsDevelopment())
60
+ {
61
+ app.UseSwagger();
62
+ app.UseSwaggerUI();
63
+ }
64
+
65
+ app.UseHttpsRedirection();
66
+ app.UseAuthentication();
67
+ app.UseAuthorization();
68
+ app.MapControllers();
69
+
70
+ // Minimale API-endpoints
71
+ app.MapGroup("/api/v1").MapUserEndpoints();
72
+
73
+ app.Run();
74
+ ```
75
+
76
+ ### EF Core-entiteit + DbContext
77
+ ```csharp
78
+ // Models/User.cs
79
+ public class User
80
+ {
81
+ public Guid Id { get; set; } = Guid.NewGuid();
82
+ public required string Email { get; set; }
83
+ public required string PasswordHash { get; set; }
84
+ public bool IsActive { get; set; } = true;
85
+ public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
86
+
87
+ public ICollection<Post> Posts { get; set; } = [];
88
+ }
89
+
90
+ // Data/AppDbContext.cs
91
+ public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
92
+ {
93
+ public DbSet<User> Users => Set<User>();
94
+ public DbSet<Post> Posts => Set<Post>();
95
+
96
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
97
+ {
98
+ modelBuilder.Entity<User>(e =>
99
+ {
100
+ e.HasIndex(u => u.Email).IsUnique();
101
+ e.Property(u => u.Email).HasMaxLength(320);
102
+ });
103
+ }
104
+ }
105
+ ```
106
+
107
+ ### Dependency injection — services
108
+ ```csharp
109
+ // Services/IUserService.cs
110
+ public interface IUserService
111
+ {
112
+ Task<UserDto> GetByIdAsync(Guid id, CancellationToken ct = default);
113
+ Task<UserDto> CreateAsync(CreateUserRequest request, CancellationToken ct = default);
114
+ }
115
+
116
+ // Services/UserService.cs
117
+ public class UserService(AppDbContext db) : IUserService
118
+ {
119
+ public async Task<UserDto> GetByIdAsync(Guid id, CancellationToken ct = default)
120
+ {
121
+ var user = await db.Users
122
+ .AsNoTracking()
123
+ .FirstOrDefaultAsync(u => u.Id == id, ct)
124
+ ?? throw new NotFoundException($"User {id} not found");
125
+
126
+ return new UserDto(user.Id, user.Email, user.CreatedAt);
127
+ }
128
+
129
+ public async Task<UserDto> CreateAsync(CreateUserRequest request, CancellationToken ct = default)
130
+ {
131
+ if (await db.Users.AnyAsync(u => u.Email == request.Email, ct))
132
+ throw new ConflictException("Email already in use");
133
+
134
+ var user = new User
135
+ {
136
+ Email = request.Email,
137
+ PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.Password)
138
+ };
139
+ db.Users.Add(user);
140
+ await db.SaveChangesAsync(ct);
141
+ return new UserDto(user.Id, user.Email, user.CreatedAt);
142
+ }
143
+ }
144
+ ```
145
+
146
+ ### Op controllers gebaseerde API
147
+ ```csharp
148
+ [ApiController]
149
+ [Route("api/v1/[controller]")]
150
+ public class UsersController(IUserService userService) : ControllerBase
151
+ {
152
+ [HttpGet("{id:guid}")]
153
+ [ProducesResponseType<UserDto>(StatusCodes.Status200OK)]
154
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
155
+ public async Task<IActionResult> GetUser(Guid id, CancellationToken ct)
156
+ {
157
+ var user = await userService.GetByIdAsync(id, ct);
158
+ return Ok(user);
159
+ }
160
+
161
+ [HttpPost]
162
+ [ProducesResponseType<UserDto>(StatusCodes.Status201Created)]
163
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
164
+ [ProducesResponseType(StatusCodes.Status409Conflict)]
165
+ public async Task<IActionResult> CreateUser(
166
+ [FromBody] CreateUserRequest request,
167
+ CancellationToken ct)
168
+ {
169
+ var user = await userService.CreateAsync(request, ct);
170
+ return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
171
+ }
172
+ }
173
+ ```
174
+
175
+ ### Minimale API-endpoints (extensiemethodepatroon)
176
+ ```csharp
177
+ // Endpoints/UserEndpoints.cs
178
+ public static class UserEndpoints
179
+ {
180
+ public static RouteGroupBuilder MapUserEndpoints(this RouteGroupBuilder group)
181
+ {
182
+ var users = group.MapGroup("/users").WithTags("Users");
183
+
184
+ users.MapGet("/{id:guid}", async (Guid id, IUserService svc, CancellationToken ct) =>
185
+ {
186
+ var user = await svc.GetByIdAsync(id, ct);
187
+ return Results.Ok(user);
188
+ })
189
+ .WithName("GetUser")
190
+ .Produces<UserDto>();
191
+
192
+ return group;
193
+ }
194
+ }
195
+ ```
196
+
197
+ ### Middleware
198
+ ```csharp
199
+ // Middleware/RequestLoggingMiddleware.cs
200
+ public class RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
201
+ {
202
+ public async Task InvokeAsync(HttpContext context)
203
+ {
204
+ var sw = Stopwatch.StartNew();
205
+ try
206
+ {
207
+ await next(context);
208
+ }
209
+ finally
210
+ {
211
+ sw.Stop();
212
+ logger.LogInformation(
213
+ "{Method} {Path} {StatusCode} in {Elapsed}ms",
214
+ context.Request.Method,
215
+ context.Request.Path,
216
+ context.Response.StatusCode,
217
+ sw.ElapsedMilliseconds);
218
+ }
219
+ }
220
+ }
221
+
222
+ // Registreer in Program.cs vóór andere middleware:
223
+ app.UseMiddleware<RequestLoggingMiddleware>();
224
+ ```
225
+
226
+ ### Achtergrondservices
227
+ ```csharp
228
+ // Services/CleanupService.cs
229
+ public class CleanupService(IServiceProvider services, ILogger<CleanupService> logger)
230
+ : BackgroundService
231
+ {
232
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
233
+ {
234
+ while (!stoppingToken.IsCancellationRequested)
235
+ {
236
+ await DoCleanupAsync(stoppingToken);
237
+ await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
238
+ }
239
+ }
240
+
241
+ private async Task DoCleanupAsync(CancellationToken ct)
242
+ {
243
+ // Gebruik een nieuwe scope voor elke iteratie — BackgroundService is singleton
244
+ using var scope = services.CreateScope();
245
+ var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
246
+ var cutoff = DateTimeOffset.UtcNow.AddDays(-30);
247
+ await db.Sessions.Where(s => s.ExpiresAt < cutoff).ExecuteDeleteAsync(ct);
248
+ }
249
+ }
250
+
251
+ // Registreer: builder.Services.AddHostedService<CleanupService>();
252
+ ```
253
+
254
+ ### LINQ-beste praktijken
255
+ ```csharp
256
+ // Gebruik altijd AsNoTracking() voor alleen-lezen queries
257
+ var users = await db.Users.AsNoTracking().Where(u => u.IsActive).ToListAsync(ct);
258
+
259
+ // Selecteer alleen benodigde kolommen — vermijd laden van volledige entiteiten voor projecties
260
+ var emails = await db.Users
261
+ .Where(u => u.IsActive)
262
+ .Select(u => u.Email)
263
+ .ToListAsync(ct);
264
+
265
+ // Gebruik ExecuteUpdateAsync/ExecuteDeleteAsync voor bulk-operaties — slaat laden van entiteiten over
266
+ await db.Users
267
+ .Where(u => !u.IsActive && u.CreatedAt < cutoff)
268
+ .ExecuteDeleteAsync(ct);
269
+
270
+ // Vermijd N+1: gebruik Include() voor gerelateerde data
271
+ var posts = await db.Posts
272
+ .Include(p => p.Author)
273
+ .Include(p => p.Tags)
274
+ .Where(p => p.Published)
275
+ .AsNoTracking()
276
+ .ToListAsync(ct);
277
+ ```
278
+
279
+ ### DTO's en records
280
+ ```csharp
281
+ // Gebruik records voor onveranderlijke DTO's
282
+ public record UserDto(Guid Id, string Email, DateTimeOffset CreatedAt);
283
+ public record CreateUserRequest(
284
+ [property: Required, EmailAddress] string Email,
285
+ [property: Required, MinLength(8)] string Password
286
+ );
287
+
288
+ // Antwoordtypen met problem details (ingebouwd .NET)
289
+ // Retourneer Results.Problem() of gooi uitzonderingen gevangen door middleware
290
+ ```
291
+
292
+ ## Voorbeeld
293
+
294
+ **Gebruiker:** Voeg een `BlogPost`-resource toe aan een .NET Web API: CRUD-endpoints, EF Core-entiteit, migraties en een achtergrondtaak die geplande berichten publiceert.
295
+
296
+ **Verwachte output:**
297
+ - `Models/BlogPost.cs` — entiteit met `Id`, `Title`, `Body`, `AuthorId` (FK naar User), `PublishedAt` (nullable), `ScheduledFor` (nullable)
298
+ - `DTOs/BlogPostDtos.cs` — `BlogPostDto`-record, `CreateBlogPostRequest`-record met `[Required]`-validatie
299
+ - `Services/IBlogPostService.cs` + `BlogPostService.cs` — CRUD-methoden, `GetPendingScheduledAsync` voor achtergrondtaak
300
+ - `Controllers/BlogPostsController.cs` — alle CRUD met juiste statuscodes
301
+ - `Services/PostPublisherService.cs` — `BackgroundService` die elke minuut controleert en vervallen berichten publiceert
302
+ - EF Core-migratie: `dotnet ef migrations add AddBlogPosts`
303
+
304
+ ---
305
+
306
+ > **Werk met ons:** Claudient wordt ondersteund door [Uitbreiden](https://uitbreiden.com/) — we bouwen AI-producten en B2B-oplossingen met ontwikkelaarsgemeenschappen. [uitbreiden.com](https://uitbreiden.com/)