@malamute/ai-rules 1.0.0 → 1.3.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 (145) hide show
  1. package/README.md +272 -121
  2. package/bin/cli.js +5 -2
  3. package/configs/_shared/CLAUDE.md +52 -149
  4. package/configs/_shared/rules/conventions/documentation.md +324 -0
  5. package/configs/_shared/rules/conventions/git.md +265 -0
  6. package/configs/_shared/rules/conventions/npm.md +80 -0
  7. package/configs/_shared/{.claude/rules → rules/conventions}/performance.md +1 -1
  8. package/configs/_shared/rules/conventions/principles.md +334 -0
  9. package/configs/_shared/rules/devops/ci-cd.md +262 -0
  10. package/configs/_shared/rules/devops/docker.md +275 -0
  11. package/configs/_shared/rules/devops/nx.md +194 -0
  12. package/configs/_shared/rules/domain/backend/api-design.md +203 -0
  13. package/configs/_shared/rules/lang/csharp/async.md +220 -0
  14. package/configs/_shared/rules/lang/csharp/csharp.md +314 -0
  15. package/configs/_shared/rules/lang/csharp/linq.md +210 -0
  16. package/configs/_shared/rules/lang/python/async.md +337 -0
  17. package/configs/_shared/rules/lang/python/celery.md +476 -0
  18. package/configs/_shared/rules/lang/python/config.md +339 -0
  19. package/configs/{python/.claude/rules → _shared/rules/lang/python}/database/sqlalchemy.md +6 -1
  20. package/configs/_shared/rules/lang/python/deployment.md +523 -0
  21. package/configs/_shared/rules/lang/python/error-handling.md +330 -0
  22. package/configs/_shared/rules/lang/python/migrations.md +421 -0
  23. package/configs/_shared/rules/lang/python/python.md +172 -0
  24. package/configs/_shared/rules/lang/python/repository.md +383 -0
  25. package/configs/{python/.claude/rules → _shared/rules/lang/python}/testing.md +2 -69
  26. package/configs/_shared/rules/lang/typescript/async.md +447 -0
  27. package/configs/_shared/rules/lang/typescript/generics.md +356 -0
  28. package/configs/_shared/rules/lang/typescript/typescript.md +212 -0
  29. package/configs/_shared/rules/quality/error-handling.md +48 -0
  30. package/configs/_shared/rules/quality/logging.md +45 -0
  31. package/configs/_shared/rules/quality/observability.md +240 -0
  32. package/configs/_shared/rules/quality/testing-patterns.md +65 -0
  33. package/configs/_shared/rules/security/secrets-management.md +222 -0
  34. package/configs/_shared/skills/analysis/explore/SKILL.md +257 -0
  35. package/configs/_shared/skills/analysis/security-audit/SKILL.md +184 -0
  36. package/configs/_shared/skills/dev/api-endpoint/SKILL.md +126 -0
  37. package/configs/_shared/{.claude/commands/generate-tests.md → skills/dev/generate-tests/SKILL.md} +6 -0
  38. package/configs/_shared/{.claude/commands/fix-issue.md → skills/git/fix-issue/SKILL.md} +6 -0
  39. package/configs/_shared/{.claude/commands/review-pr.md → skills/git/review-pr/SKILL.md} +6 -0
  40. package/configs/_shared/skills/infra/deploy/SKILL.md +139 -0
  41. package/configs/_shared/skills/infra/docker/SKILL.md +95 -0
  42. package/configs/_shared/skills/infra/migration/SKILL.md +158 -0
  43. package/configs/_shared/skills/nx/nx-affected/SKILL.md +72 -0
  44. package/configs/_shared/skills/nx/nx-lib/SKILL.md +375 -0
  45. package/configs/angular/CLAUDE.md +24 -216
  46. package/configs/angular/{.claude/rules → rules/core}/components.md +69 -15
  47. package/configs/angular/rules/core/resource.md +285 -0
  48. package/configs/angular/rules/core/signals.md +323 -0
  49. package/configs/angular/rules/http.md +338 -0
  50. package/configs/angular/rules/routing.md +291 -0
  51. package/configs/angular/rules/ssr.md +312 -0
  52. package/configs/angular/rules/state/signal-store.md +408 -0
  53. package/configs/angular/{.claude/rules → rules/state}/state.md +2 -2
  54. package/configs/angular/{.claude/rules → rules}/testing.md +7 -7
  55. package/configs/angular/rules/ui/aria.md +422 -0
  56. package/configs/angular/rules/ui/forms.md +424 -0
  57. package/configs/angular/rules/ui/pipes-directives.md +335 -0
  58. package/configs/angular/{.claude/settings.json → settings.json} +3 -0
  59. package/configs/dotnet/CLAUDE.md +53 -286
  60. package/configs/dotnet/rules/background-services.md +552 -0
  61. package/configs/dotnet/rules/configuration.md +426 -0
  62. package/configs/dotnet/rules/ddd.md +447 -0
  63. package/configs/dotnet/rules/dependency-injection.md +343 -0
  64. package/configs/dotnet/rules/mediatr.md +320 -0
  65. package/configs/dotnet/rules/middleware.md +489 -0
  66. package/configs/dotnet/rules/result-pattern.md +363 -0
  67. package/configs/dotnet/rules/validation.md +388 -0
  68. package/configs/dotnet/settings.json +29 -0
  69. package/configs/fastapi/CLAUDE.md +144 -0
  70. package/configs/fastapi/rules/background-tasks.md +254 -0
  71. package/configs/fastapi/rules/dependencies.md +170 -0
  72. package/configs/{python/.claude → fastapi}/rules/fastapi.md +61 -1
  73. package/configs/fastapi/rules/lifespan.md +274 -0
  74. package/configs/fastapi/rules/middleware.md +229 -0
  75. package/configs/fastapi/rules/pydantic.md +433 -0
  76. package/configs/fastapi/rules/responses.md +251 -0
  77. package/configs/fastapi/rules/routers.md +202 -0
  78. package/configs/fastapi/rules/security.md +222 -0
  79. package/configs/fastapi/rules/testing.md +251 -0
  80. package/configs/fastapi/rules/websockets.md +298 -0
  81. package/configs/fastapi/settings.json +35 -0
  82. package/configs/flask/CLAUDE.md +166 -0
  83. package/configs/flask/rules/blueprints.md +208 -0
  84. package/configs/flask/rules/cli.md +285 -0
  85. package/configs/flask/rules/configuration.md +281 -0
  86. package/configs/flask/rules/context.md +238 -0
  87. package/configs/flask/rules/error-handlers.md +278 -0
  88. package/configs/flask/rules/extensions.md +278 -0
  89. package/configs/flask/rules/flask.md +171 -0
  90. package/configs/flask/rules/marshmallow.md +206 -0
  91. package/configs/flask/rules/security.md +267 -0
  92. package/configs/flask/rules/testing.md +284 -0
  93. package/configs/flask/settings.json +35 -0
  94. package/configs/nestjs/CLAUDE.md +57 -215
  95. package/configs/nestjs/rules/common-patterns.md +300 -0
  96. package/configs/nestjs/rules/filters.md +376 -0
  97. package/configs/nestjs/rules/interceptors.md +317 -0
  98. package/configs/nestjs/rules/middleware.md +321 -0
  99. package/configs/nestjs/{.claude/rules → rules}/modules.md +26 -0
  100. package/configs/nestjs/rules/pipes.md +351 -0
  101. package/configs/nestjs/rules/websockets.md +451 -0
  102. package/configs/nestjs/settings.json +31 -0
  103. package/configs/nextjs/CLAUDE.md +69 -331
  104. package/configs/nextjs/rules/api-routes.md +358 -0
  105. package/configs/nextjs/rules/authentication.md +355 -0
  106. package/configs/nextjs/{.claude/rules → rules}/components.md +52 -0
  107. package/configs/nextjs/rules/data-fetching.md +249 -0
  108. package/configs/nextjs/rules/database.md +400 -0
  109. package/configs/nextjs/rules/middleware.md +303 -0
  110. package/configs/nextjs/rules/routing.md +324 -0
  111. package/configs/nextjs/rules/seo.md +350 -0
  112. package/configs/nextjs/rules/server-actions.md +353 -0
  113. package/configs/nextjs/{.claude/rules → rules}/state/zustand.md +6 -6
  114. package/configs/nextjs/{.claude/settings.json → settings.json} +7 -0
  115. package/package.json +24 -9
  116. package/src/cli.js +218 -0
  117. package/src/config.js +63 -0
  118. package/src/index.js +4 -0
  119. package/src/installer.js +414 -0
  120. package/src/merge.js +109 -0
  121. package/src/tech-config.json +45 -0
  122. package/src/utils.js +88 -0
  123. package/configs/dotnet/.claude/settings.json +0 -9
  124. package/configs/nestjs/.claude/settings.json +0 -15
  125. package/configs/python/.claude/rules/flask.md +0 -332
  126. package/configs/python/.claude/settings.json +0 -18
  127. package/configs/python/CLAUDE.md +0 -273
  128. package/src/install.js +0 -315
  129. /package/configs/_shared/{.claude/rules → rules/domain/frontend}/accessibility.md +0 -0
  130. /package/configs/_shared/{.claude/rules → rules/security}/security.md +0 -0
  131. /package/configs/_shared/{.claude/skills → skills/dev}/debug/SKILL.md +0 -0
  132. /package/configs/_shared/{.claude/skills → skills/dev}/learning/SKILL.md +0 -0
  133. /package/configs/_shared/{.claude/skills → skills/dev}/spec/SKILL.md +0 -0
  134. /package/configs/_shared/{.claude/skills → skills/git}/review/SKILL.md +0 -0
  135. /package/configs/dotnet/{.claude/rules → rules}/api.md +0 -0
  136. /package/configs/dotnet/{.claude/rules → rules}/architecture.md +0 -0
  137. /package/configs/dotnet/{.claude/rules → rules}/database/efcore.md +0 -0
  138. /package/configs/dotnet/{.claude/rules → rules}/testing.md +0 -0
  139. /package/configs/nestjs/{.claude/rules → rules}/auth.md +0 -0
  140. /package/configs/nestjs/{.claude/rules → rules}/database/prisma.md +0 -0
  141. /package/configs/nestjs/{.claude/rules → rules}/database/typeorm.md +0 -0
  142. /package/configs/nestjs/{.claude/rules → rules}/testing.md +0 -0
  143. /package/configs/nestjs/{.claude/rules → rules}/validation.md +0 -0
  144. /package/configs/nextjs/{.claude/rules → rules}/state/redux-toolkit.md +0 -0
  145. /package/configs/nextjs/{.claude/rules → rules}/testing.md +0 -0
@@ -0,0 +1,314 @@
1
+ ---
2
+ paths:
3
+ - "**/*.cs"
4
+ - "**/*.csproj"
5
+ ---
6
+
7
+ # C# Code Style Rules
8
+
9
+ ## Nullability
10
+
11
+ Enable nullable reference types in all projects:
12
+
13
+ ```xml
14
+ <PropertyGroup>
15
+ <Nullable>enable</Nullable>
16
+ </PropertyGroup>
17
+ ```
18
+
19
+ ```csharp
20
+ // GOOD - explicit nullability
21
+ public string Name { get; set; } = string.Empty;
22
+ public string? MiddleName { get; set; }
23
+
24
+ // BAD - ambiguous
25
+ public string Name { get; set; } // Warning: non-nullable not initialized
26
+ ```
27
+
28
+ ## Naming Conventions
29
+
30
+ | Element | Convention | Example |
31
+ |---------|------------|---------|
32
+ | Classes | PascalCase | `UserService` |
33
+ | Interfaces | IPascalCase | `IUserRepository` |
34
+ | Methods | PascalCase | `GetUserById` |
35
+ | Properties | PascalCase | `FirstName` |
36
+ | Private fields | _camelCase | `_userRepository` |
37
+ | Local variables | camelCase | `currentUser` |
38
+ | Constants | PascalCase | `MaxRetryCount` |
39
+ | Async methods | Suffix `Async` | `GetUserAsync` |
40
+
41
+ ```csharp
42
+ // GOOD
43
+ private readonly IUserRepository _userRepository;
44
+ public async Task<User?> GetUserByIdAsync(int id) { }
45
+
46
+ // BAD
47
+ private IUserRepository userRepository; // Missing underscore
48
+ public async Task<User?> GetUserById(int id) { } // Missing Async suffix
49
+ ```
50
+
51
+ ## Type Guidelines
52
+
53
+ ### Use `var` When Type Is Obvious
54
+
55
+ ```csharp
56
+ // GOOD - type is obvious
57
+ var user = new User();
58
+ var users = await _repository.GetAllAsync();
59
+ var count = items.Count;
60
+
61
+ // GOOD - type not obvious, be explicit
62
+ User user = GetUser();
63
+ IEnumerable<string> names = GetNames();
64
+
65
+ // BAD - redundant
66
+ User user = new User();
67
+ ```
68
+
69
+ ### Prefer Collection Expressions (C# 12+)
70
+
71
+ ```csharp
72
+ // GOOD - collection expressions
73
+ int[] numbers = [1, 2, 3];
74
+ List<string> names = ["Alice", "Bob"];
75
+ Dictionary<string, int> ages = new() { ["Alice"] = 30 };
76
+
77
+ // BAD - verbose
78
+ int[] numbers = new int[] { 1, 2, 3 };
79
+ List<string> names = new List<string> { "Alice", "Bob" };
80
+ ```
81
+
82
+ ### Use Pattern Matching
83
+
84
+ ```csharp
85
+ // GOOD - pattern matching
86
+ if (user is { IsActive: true, Role: "Admin" })
87
+ {
88
+ // ...
89
+ }
90
+
91
+ return status switch
92
+ {
93
+ OrderStatus.Pending => "Awaiting payment",
94
+ OrderStatus.Shipped => "On the way",
95
+ OrderStatus.Delivered => "Completed",
96
+ _ => "Unknown"
97
+ };
98
+
99
+ // BAD - verbose checks
100
+ if (user != null && user.IsActive && user.Role == "Admin")
101
+ ```
102
+
103
+ ## Async/Await
104
+
105
+ ### Always Use Async All The Way
106
+
107
+ ```csharp
108
+ // GOOD - async all the way
109
+ public async Task<User> GetUserAsync(int id)
110
+ {
111
+ return await _repository.GetByIdAsync(id);
112
+ }
113
+
114
+ // BAD - blocking on async
115
+ public User GetUser(int id)
116
+ {
117
+ return _repository.GetByIdAsync(id).Result; // Deadlock risk!
118
+ }
119
+
120
+ // BAD - async void (except event handlers)
121
+ public async void ProcessData() // Can't await, exceptions lost
122
+ ```
123
+
124
+ ### ConfigureAwait in Libraries
125
+
126
+ ```csharp
127
+ // In library code (not ASP.NET Core)
128
+ await SomeOperationAsync().ConfigureAwait(false);
129
+
130
+ // In ASP.NET Core controllers - not needed
131
+ await SomeOperationAsync();
132
+ ```
133
+
134
+ ### Cancellation Tokens
135
+
136
+ ```csharp
137
+ // GOOD - propagate cancellation
138
+ public async Task<List<User>> GetUsersAsync(CancellationToken cancellationToken = default)
139
+ {
140
+ return await _context.Users
141
+ .Where(u => u.IsActive)
142
+ .ToListAsync(cancellationToken);
143
+ }
144
+
145
+ // BAD - ignoring cancellation
146
+ public async Task<List<User>> GetUsersAsync()
147
+ {
148
+ return await _context.Users.ToListAsync();
149
+ }
150
+ ```
151
+
152
+ ## LINQ Best Practices
153
+
154
+ ```csharp
155
+ // GOOD - method syntax for complex queries
156
+ var activeAdmins = users
157
+ .Where(u => u.IsActive)
158
+ .Where(u => u.Role == Role.Admin)
159
+ .OrderBy(u => u.Name)
160
+ .Select(u => new UserDto(u.Id, u.Name))
161
+ .ToList();
162
+
163
+ // GOOD - query syntax for joins
164
+ var result = from order in orders
165
+ join customer in customers on order.CustomerId equals customer.Id
166
+ select new { order.Id, customer.Name };
167
+
168
+ // BAD - multiple enumerations
169
+ var users = GetUsers();
170
+ var count = users.Count(); // First enumeration
171
+ var first = users.First(); // Second enumeration
172
+
173
+ // GOOD - materialize once
174
+ var users = GetUsers().ToList();
175
+ var count = users.Count;
176
+ var first = users[0];
177
+ ```
178
+
179
+ ## Records and Immutability
180
+
181
+ ```csharp
182
+ // GOOD - immutable DTOs with records
183
+ public record UserDto(int Id, string Name, string Email);
184
+
185
+ public record CreateUserCommand(string Name, string Email)
186
+ {
187
+ public DateTime CreatedAt { get; init; } = DateTime.UtcNow;
188
+ }
189
+
190
+ // GOOD - with-expressions for modifications
191
+ var updated = user with { Name = "New Name" };
192
+
193
+ // BAD - mutable DTOs
194
+ public class UserDto
195
+ {
196
+ public int Id { get; set; }
197
+ public string Name { get; set; }
198
+ }
199
+ ```
200
+
201
+ ## Error Handling
202
+
203
+ ### Specific Exceptions
204
+
205
+ ```csharp
206
+ // GOOD - specific exceptions
207
+ public User GetUser(int id)
208
+ {
209
+ return _repository.GetById(id)
210
+ ?? throw new NotFoundException($"User {id} not found");
211
+ }
212
+
213
+ // BAD - generic exception
214
+ throw new Exception("User not found");
215
+ ```
216
+
217
+ ### Never Swallow Exceptions
218
+
219
+ ```csharp
220
+ // BAD - silent failure
221
+ try
222
+ {
223
+ await _service.ProcessAsync();
224
+ }
225
+ catch (Exception)
226
+ {
227
+ // Swallowed
228
+ }
229
+
230
+ // GOOD - log and handle
231
+ try
232
+ {
233
+ await _service.ProcessAsync();
234
+ }
235
+ catch (Exception ex)
236
+ {
237
+ _logger.LogError(ex, "Failed to process");
238
+ throw;
239
+ }
240
+ ```
241
+
242
+ ## Disposables
243
+
244
+ ```csharp
245
+ // GOOD - using declaration (C# 8+)
246
+ await using var connection = new SqlConnection(connectionString);
247
+ await connection.OpenAsync();
248
+
249
+ // GOOD - using statement for limited scope
250
+ using (var stream = File.OpenRead(path))
251
+ {
252
+ // ...
253
+ }
254
+ // Stream disposed here
255
+
256
+ // BAD - manual disposal
257
+ var connection = new SqlConnection(connectionString);
258
+ try
259
+ {
260
+ await connection.OpenAsync();
261
+ }
262
+ finally
263
+ {
264
+ connection.Dispose();
265
+ }
266
+ ```
267
+
268
+ ## File-Scoped Namespaces
269
+
270
+ ```csharp
271
+ // GOOD - file-scoped (C# 10+)
272
+ namespace MyApp.Services;
273
+
274
+ public class UserService
275
+ {
276
+ // ...
277
+ }
278
+
279
+ // BAD - block-scoped (unnecessary nesting)
280
+ namespace MyApp.Services
281
+ {
282
+ public class UserService
283
+ {
284
+ // ...
285
+ }
286
+ }
287
+ ```
288
+
289
+ ## Primary Constructors (C# 12+)
290
+
291
+ ```csharp
292
+ // GOOD - primary constructor
293
+ public class UserService(IUserRepository repository, ILogger<UserService> logger)
294
+ {
295
+ public async Task<User?> GetUserAsync(int id)
296
+ {
297
+ logger.LogInformation("Getting user {Id}", id);
298
+ return await repository.GetByIdAsync(id);
299
+ }
300
+ }
301
+
302
+ // Also valid - traditional constructor with readonly fields
303
+ public class UserService
304
+ {
305
+ private readonly IUserRepository _repository;
306
+ private readonly ILogger<UserService> _logger;
307
+
308
+ public UserService(IUserRepository repository, ILogger<UserService> logger)
309
+ {
310
+ _repository = repository;
311
+ _logger = logger;
312
+ }
313
+ }
314
+ ```
@@ -0,0 +1,210 @@
1
+ ---
2
+ paths:
3
+ - "**/*.cs"
4
+ ---
5
+
6
+ # LINQ Best Practices
7
+
8
+ ## Method vs Query Syntax
9
+
10
+ ```csharp
11
+ // Method syntax - preferred for simple queries
12
+ var activeUsers = users
13
+ .Where(u => u.IsActive)
14
+ .OrderBy(u => u.Name)
15
+ .ToList();
16
+
17
+ // Query syntax - better for joins and complex queries
18
+ var orderDetails = from order in orders
19
+ join customer in customers
20
+ on order.CustomerId equals customer.Id
21
+ join product in products
22
+ on order.ProductId equals product.Id
23
+ where order.Status == OrderStatus.Completed
24
+ select new
25
+ {
26
+ OrderId = order.Id,
27
+ CustomerName = customer.Name,
28
+ ProductName = product.Name
29
+ };
30
+ ```
31
+
32
+ ## Deferred vs Immediate Execution
33
+
34
+ ```csharp
35
+ // Deferred - query not executed until enumerated
36
+ var query = users.Where(u => u.IsActive); // No DB call yet
37
+
38
+ // Immediate - forces execution
39
+ var list = users.Where(u => u.IsActive).ToList(); // Executes now
40
+ var array = users.Where(u => u.IsActive).ToArray(); // Executes now
41
+ var first = users.First(u => u.IsActive); // Executes now
42
+ var count = users.Count(u => u.IsActive); // Executes now
43
+
44
+ // BAD - multiple enumeration
45
+ IEnumerable<User> users = GetUsers();
46
+ if (users.Any()) // First enumeration
47
+ {
48
+ var first = users.First(); // Second enumeration - might get different data!
49
+ }
50
+
51
+ // GOOD - materialize once
52
+ var users = GetUsers().ToList();
53
+ if (users.Count > 0)
54
+ {
55
+ var first = users[0];
56
+ }
57
+ ```
58
+
59
+ ## Filtering
60
+
61
+ ```csharp
62
+ // Chain Where for readability
63
+ var results = items
64
+ .Where(x => x.IsActive)
65
+ .Where(x => x.Category == "Electronics")
66
+ .Where(x => x.Price > 100);
67
+
68
+ // Use Any/All for existence checks
69
+ if (users.Any(u => u.Role == Role.Admin)) // GOOD
70
+ if (users.Where(u => u.Role == Role.Admin).Count() > 0) // BAD
71
+
72
+ // FirstOrDefault with predicate
73
+ var admin = users.FirstOrDefault(u => u.Role == Role.Admin);
74
+
75
+ // SingleOrDefault when expecting 0 or 1
76
+ var user = users.SingleOrDefault(u => u.Email == email);
77
+ ```
78
+
79
+ ## Projection
80
+
81
+ ```csharp
82
+ // Select for transformation
83
+ var dtos = users.Select(u => new UserDto(u.Id, u.Name));
84
+
85
+ // SelectMany to flatten
86
+ var allOrders = customers.SelectMany(c => c.Orders);
87
+
88
+ // Anonymous types for intermediate results
89
+ var intermediate = users
90
+ .Select(u => new { u.Id, FullName = $"{u.FirstName} {u.LastName}" })
91
+ .Where(x => x.FullName.StartsWith("A"));
92
+ ```
93
+
94
+ ## Grouping
95
+
96
+ ```csharp
97
+ // GroupBy
98
+ var usersByRole = users
99
+ .GroupBy(u => u.Role)
100
+ .Select(g => new
101
+ {
102
+ Role = g.Key,
103
+ Count = g.Count(),
104
+ Users = g.ToList()
105
+ });
106
+
107
+ // ToLookup - immediate execution, allows multiple enumeration
108
+ var lookup = users.ToLookup(u => u.Role);
109
+ var admins = lookup[Role.Admin];
110
+ var managers = lookup[Role.Manager];
111
+ ```
112
+
113
+ ## Aggregation
114
+
115
+ ```csharp
116
+ // Aggregate functions
117
+ var total = orders.Sum(o => o.Amount);
118
+ var average = orders.Average(o => o.Amount);
119
+ var max = orders.Max(o => o.Amount);
120
+ var min = orders.Min(o => o.Amount);
121
+
122
+ // Aggregate for custom accumulation
123
+ var concatenated = names.Aggregate((a, b) => $"{a}, {b}");
124
+
125
+ // With seed value
126
+ var total = items.Aggregate(
127
+ seed: 0m,
128
+ func: (sum, item) => sum + item.Price * item.Quantity);
129
+ ```
130
+
131
+ ## Set Operations
132
+
133
+ ```csharp
134
+ // Distinct
135
+ var uniqueCategories = products.Select(p => p.Category).Distinct();
136
+
137
+ // DistinctBy (C# 10+)
138
+ var uniqueByName = products.DistinctBy(p => p.Name);
139
+
140
+ // Union, Intersect, Except
141
+ var allIds = list1.Select(x => x.Id).Union(list2.Select(x => x.Id));
142
+ var commonIds = list1.Select(x => x.Id).Intersect(list2.Select(x => x.Id));
143
+ var onlyInFirst = list1.Select(x => x.Id).Except(list2.Select(x => x.Id));
144
+ ```
145
+
146
+ ## Ordering
147
+
148
+ ```csharp
149
+ // Multiple sort criteria
150
+ var sorted = users
151
+ .OrderBy(u => u.LastName)
152
+ .ThenBy(u => u.FirstName)
153
+ .ThenByDescending(u => u.CreatedAt);
154
+
155
+ // Order vs OrderBy (C# 11+)
156
+ var ordered = items.Order(); // Uses default comparer
157
+ var orderedDesc = items.OrderDescending();
158
+ ```
159
+
160
+ ## Pagination
161
+
162
+ ```csharp
163
+ // Skip/Take for pagination
164
+ var page = users
165
+ .OrderBy(u => u.Id)
166
+ .Skip((pageNumber - 1) * pageSize)
167
+ .Take(pageSize)
168
+ .ToList();
169
+
170
+ // Chunk for batching (C# 10+)
171
+ var batches = items.Chunk(100);
172
+ foreach (var batch in batches)
173
+ {
174
+ await ProcessBatchAsync(batch);
175
+ }
176
+ ```
177
+
178
+ ## EF Core Specific
179
+
180
+ ```csharp
181
+ // GOOD - let EF translate to SQL
182
+ var users = await _context.Users
183
+ .Where(u => u.IsActive)
184
+ .OrderBy(u => u.Name)
185
+ .ToListAsync();
186
+
187
+ // BAD - client-side evaluation (throws in EF Core 3+)
188
+ var users = await _context.Users
189
+ .Where(u => SomeLocalMethod(u)) // Can't translate
190
+ .ToListAsync();
191
+
192
+ // GOOD - explicit client evaluation when needed
193
+ var users = await _context.Users
194
+ .Where(u => u.IsActive)
195
+ .AsEnumerable() // Switch to client
196
+ .Where(u => SomeLocalMethod(u))
197
+ .ToList();
198
+
199
+ // Use AsNoTracking for read-only queries
200
+ var users = await _context.Users
201
+ .AsNoTracking()
202
+ .Where(u => u.IsActive)
203
+ .ToListAsync();
204
+
205
+ // Projection to avoid over-fetching
206
+ var dtos = await _context.Users
207
+ .Where(u => u.IsActive)
208
+ .Select(u => new UserDto(u.Id, u.Name, u.Email))
209
+ .ToListAsync();
210
+ ```