@ngxtm/devkit 3.4.0 → 3.4.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.
- package/package.json +2 -1
- package/rules/README.md +141 -0
- package/rules/dart/best-practices/SKILL.md +23 -0
- package/rules/dart/language/SKILL.md +52 -0
- package/rules/dart/tooling/SKILL.md +43 -0
- package/rules/dotnet/aspnet-core/SKILL.md +92 -0
- package/rules/dotnet/aspnet-core/references/REFERENCE.md +335 -0
- package/rules/dotnet/best-practices/SKILL.md +101 -0
- package/rules/dotnet/best-practices/references/REFERENCE.md +256 -0
- package/rules/dotnet/blazor/SKILL.md +146 -0
- package/rules/dotnet/blazor/references/REFERENCE.md +392 -0
- package/rules/dotnet/language/SKILL.md +82 -0
- package/rules/dotnet/language/references/REFERENCE.md +222 -0
- package/rules/dotnet/patterns.rule.md +388 -0
- package/rules/dotnet/razor-pages/SKILL.md +124 -0
- package/rules/dotnet/razor-pages/references/REFERENCE.md +321 -0
- package/rules/dotnet/security/SKILL.md +89 -0
- package/rules/dotnet/security/references/REFERENCE.md +295 -0
- package/rules/dotnet/tooling/SKILL.md +92 -0
- package/rules/dotnet/tooling/references/REFERENCE.md +300 -0
- package/rules/flutter/auto-route-navigation/SKILL.md +43 -0
- package/rules/flutter/auto-route-navigation/references/REFERENCE.md +19 -0
- package/rules/flutter/auto-route-navigation/references/router-config.md +62 -0
- package/rules/flutter/bloc-state-management/SKILL.md +64 -0
- package/rules/flutter/bloc-state-management/references/REFERENCE.md +20 -0
- package/rules/flutter/bloc-state-management/references/auth-bloc-example.md +52 -0
- package/rules/flutter/bloc-state-management/references/equatable-usage.md +56 -0
- package/rules/flutter/bloc-state-management/references/property-based-state.md +68 -0
- package/rules/flutter/bloc.rule.md +76 -0
- package/rules/flutter/cicd/SKILL.md +48 -0
- package/rules/flutter/cicd/references/advanced-workflow.md +66 -0
- package/rules/flutter/cicd/references/fastlane.md +139 -0
- package/rules/flutter/cicd/references/github-actions.md +59 -0
- package/rules/flutter/dependency-injection/SKILL.md +42 -0
- package/rules/flutter/dependency-injection/references/REFERENCE.md +15 -0
- package/rules/flutter/dependency-injection/references/modules.md +37 -0
- package/rules/flutter/error-handling/SKILL.md +32 -0
- package/rules/flutter/error-handling/references/REFERENCE.md +19 -0
- package/rules/flutter/error-handling/references/error-mapping.md +31 -0
- package/rules/flutter/feature-based-clean-architecture/SKILL.md +46 -0
- package/rules/flutter/feature-based-clean-architecture/references/REFERENCE.md +14 -0
- package/rules/flutter/feature-based-clean-architecture/references/folder-structure.md +36 -0
- package/rules/flutter/getx-navigation/SKILL.md +70 -0
- package/rules/flutter/getx-navigation/references/app-pages.md +40 -0
- package/rules/flutter/getx-navigation/references/middleware-example.md +29 -0
- package/rules/flutter/getx-state-management/SKILL.md +76 -0
- package/rules/flutter/getx-state-management/references/binding-example.md +32 -0
- package/rules/flutter/getx-state-management/references/reactive-vs-simple.md +39 -0
- package/rules/flutter/go-router-navigation/SKILL.md +57 -0
- package/rules/flutter/idiomatic-flutter/SKILL.md +20 -0
- package/rules/flutter/layer-based-clean-architecture/SKILL.md +50 -0
- package/rules/flutter/layer-based-clean-architecture/references/REFERENCE.md +60 -0
- package/rules/flutter/layer-based-clean-architecture/references/repository-mapping.md +50 -0
- package/rules/flutter/localization/SKILL.md +50 -0
- package/rules/flutter/localization/references/REFERENCE.md +48 -0
- package/rules/flutter/localization/references/sheet-loader.md +33 -0
- package/rules/flutter/navigator-v1-navigation/SKILL.md +71 -0
- package/rules/flutter/navigator-v1-navigation/references/on-generate-route.md +48 -0
- package/rules/flutter/performance/SKILL.md +24 -0
- package/rules/flutter/retrofit-networking/SKILL.md +51 -0
- package/rules/flutter/retrofit-networking/references/REFERENCE.md +19 -0
- package/rules/flutter/retrofit-networking/references/token-refresh.md +40 -0
- package/rules/flutter/riverpod-state-management/SKILL.md +53 -0
- package/rules/flutter/riverpod-state-management/references/architecture.md +124 -0
- package/rules/flutter/riverpod-state-management/references/best-practices.md +89 -0
- package/rules/flutter/riverpod-state-management/references/testing.md +73 -0
- package/rules/flutter/riverpod.rule.md +78 -0
- package/rules/flutter/security/SKILL.md +33 -0
- package/rules/flutter/security/references/REFERENCE.md +15 -0
- package/rules/flutter/security/references/network-security.md +28 -0
- package/rules/flutter/testing/SKILL.md +44 -0
- package/rules/flutter/testing/references/REFERENCE.md +21 -0
- package/rules/flutter/testing/references/bloc-testing.md +38 -0
- package/rules/flutter/testing/references/integration-testing.md +128 -0
- package/rules/flutter/testing/references/robot-pattern.md +82 -0
- package/rules/flutter/testing/references/unit-testing.md +130 -0
- package/rules/flutter/testing/references/widget-testing.md +120 -0
- package/rules/flutter/widgets/SKILL.md +37 -0
- package/rules/golang/chi-router/SKILL.md +219 -0
- package/rules/golang/chi-router/references/REFERENCE.md +13 -0
- package/rules/golang/chi-router/references/routing-patterns.md +205 -0
- package/rules/golang/cobra-cli/SKILL.md +227 -0
- package/rules/golang/cobra-cli/references/REFERENCE.md +13 -0
- package/rules/golang/cobra-cli/references/command-patterns.md +224 -0
- package/rules/golang/core/SKILL.md +210 -0
- package/rules/golang/core/references/REFERENCE.md +14 -0
- package/rules/golang/core/references/concurrency-patterns.md +114 -0
- package/rules/golang/core/references/error-handling.md +87 -0
- package/rules/golang/echo-framework/SKILL.md +215 -0
- package/rules/golang/echo-framework/references/REFERENCE.md +14 -0
- package/rules/golang/echo-framework/references/middleware-patterns.md +141 -0
- package/rules/golang/echo-framework/references/routing-patterns.md +140 -0
- package/rules/golang/ent-orm/SKILL.md +239 -0
- package/rules/golang/ent-orm/references/REFERENCE.md +13 -0
- package/rules/golang/ent-orm/references/schema-patterns.md +255 -0
- package/rules/golang/fiber-framework/SKILL.md +196 -0
- package/rules/golang/fiber-framework/references/REFERENCE.md +13 -0
- package/rules/golang/fiber-framework/references/routing-patterns.md +191 -0
- package/rules/golang/gin-framework/SKILL.md +205 -0
- package/rules/golang/gin-framework/references/REFERENCE.md +14 -0
- package/rules/golang/gin-framework/references/middleware-patterns.md +119 -0
- package/rules/golang/gorm-orm/SKILL.md +196 -0
- package/rules/golang/gorm-orm/references/REFERENCE.md +14 -0
- package/rules/golang/gorm-orm/references/model-definitions.md +167 -0
- package/rules/golang/gorm-orm/references/query-patterns.md +161 -0
- package/rules/golang/grpc/SKILL.md +231 -0
- package/rules/golang/grpc/references/REFERENCE.md +13 -0
- package/rules/golang/grpc/references/service-patterns.md +276 -0
- package/rules/golang/testify/SKILL.md +239 -0
- package/rules/golang/testify/references/REFERENCE.md +13 -0
- package/rules/golang/testify/references/assert-patterns.md +170 -0
- package/rules/golang/validator/SKILL.md +234 -0
- package/rules/golang/validator/references/REFERENCE.md +13 -0
- package/rules/golang/validator/references/validation-tags.md +211 -0
- package/rules/golang/viper-config/SKILL.md +244 -0
- package/rules/golang/viper-config/references/REFERENCE.md +13 -0
- package/rules/golang/viper-config/references/config-loading.md +181 -0
- package/rules/golang/wire-di/SKILL.md +243 -0
- package/rules/golang/wire-di/references/REFERENCE.md +13 -0
- package/rules/golang/wire-di/references/provider-patterns.md +193 -0
- package/rules/golang/zap-logging/SKILL.md +203 -0
- package/rules/golang/zap-logging/references/REFERENCE.md +13 -0
- package/rules/golang/zap-logging/references/logger-config.md +165 -0
- package/rules/java/build-gradle/SKILL.md +92 -0
- package/rules/java/build-gradle/references/REFERENCE.md +14 -0
- package/rules/java/build-gradle/references/kotlin-dsl.md +118 -0
- package/rules/java/build-gradle/references/task-configuration.md +132 -0
- package/rules/java/build-maven/SKILL.md +86 -0
- package/rules/java/build-maven/references/REFERENCE.md +14 -0
- package/rules/java/build-maven/references/dependency-management.md +111 -0
- package/rules/java/build-maven/references/lifecycle-phases.md +114 -0
- package/rules/java/graalvm-native/SKILL.md +105 -0
- package/rules/java/graalvm-native/references/REFERENCE.md +12 -0
- package/rules/java/java-collections-streams/SKILL.md +148 -0
- package/rules/java/java-collections-streams/references/REFERENCE.md +15 -0
- package/rules/java/java-collections-streams/references/collectors-patterns.md +178 -0
- package/rules/java/java-collections-streams/references/stream-pipelines.md +165 -0
- package/rules/java/java-concurrency/SKILL.md +187 -0
- package/rules/java/java-concurrency/references/REFERENCE.md +17 -0
- package/rules/java/java-concurrency/references/completable-future.md +165 -0
- package/rules/java/java-concurrency/references/executor-patterns.md +176 -0
- package/rules/java/java-concurrency/references/virtual-threads.md +190 -0
- package/rules/java/java-core-language/SKILL.md +121 -0
- package/rules/java/java-core-language/references/REFERENCE.md +15 -0
- package/rules/java/java-core-language/references/jvm-memory-model.md +160 -0
- package/rules/java/java-core-language/references/modern-java-features.md +168 -0
- package/rules/java/java-project-structure/SKILL.md +195 -0
- package/rules/java/java-project-structure/references/REFERENCE.md +15 -0
- package/rules/java/java-project-structure/references/maven-project-layout.md +199 -0
- package/rules/java/java-project-structure/references/module-system.md +159 -0
- package/rules/java/micronaut-core/SKILL.md +99 -0
- package/rules/java/micronaut-core/references/REFERENCE.md +12 -0
- package/rules/java/micronaut-reactive/SKILL.md +68 -0
- package/rules/java/micronaut-reactive/references/REFERENCE.md +12 -0
- package/rules/java/quarkus-core/SKILL.md +85 -0
- package/rules/java/quarkus-core/references/REFERENCE.md +12 -0
- package/rules/java/quarkus-reactive/SKILL.md +67 -0
- package/rules/java/quarkus-reactive/references/REFERENCE.md +12 -0
- package/rules/java/spring-batch/SKILL.md +102 -0
- package/rules/java/spring-batch/references/REFERENCE.md +12 -0
- package/rules/java/spring-boot-architecture/SKILL.md +206 -0
- package/rules/java/spring-boot-architecture/references/REFERENCE.md +15 -0
- package/rules/java/spring-boot-architecture/references/auto-configuration.md +158 -0
- package/rules/java/spring-boot-architecture/references/configuration-properties.md +202 -0
- package/rules/java/spring-boot-web/SKILL.md +217 -0
- package/rules/java/spring-boot-web/references/REFERENCE.md +17 -0
- package/rules/java/spring-cloud/SKILL.md +109 -0
- package/rules/java/spring-cloud/references/REFERENCE.md +13 -0
- package/rules/java/spring-data-jpa/SKILL.md +241 -0
- package/rules/java/spring-data-jpa/references/REFERENCE.md +16 -0
- package/rules/java/spring-security/SKILL.md +161 -0
- package/rules/java/spring-security/references/REFERENCE.md +16 -0
- package/rules/java/spring-security/references/jwt-auth-flow.md +213 -0
- package/rules/java/testing-junit-mockito/SKILL.md +135 -0
- package/rules/java/testing-junit-mockito/references/REFERENCE.md +15 -0
- package/rules/java/testing-junit-mockito/references/junit5-patterns.md +159 -0
- package/rules/java/testing-junit-mockito/references/mockito-patterns.md +148 -0
- package/rules/java/testing-junit-mockito/references/spring-boot-testing.md +152 -0
- package/rules/javascript/best-practices/SKILL.md +64 -0
- package/rules/javascript/best-practices/references/REFERENCE.md +91 -0
- package/rules/javascript/language/SKILL.md +71 -0
- package/rules/javascript/language/references/REFERENCE.md +106 -0
- package/rules/javascript/tooling/SKILL.md +60 -0
- package/rules/javascript/tooling/references/REFERENCE.md +107 -0
- package/rules/metadata.json +54 -0
- package/rules/nestjs/api-standards/SKILL.md +47 -0
- package/rules/nestjs/api-standards/references/pagination-wrapper.md +87 -0
- package/rules/nestjs/architecture/SKILL.md +68 -0
- package/rules/nestjs/architecture/references/dynamic-module.md +53 -0
- package/rules/nestjs/caching/SKILL.md +51 -0
- package/rules/nestjs/caching/references/REFERENCE.md +13 -0
- package/rules/nestjs/caching/references/cache-patterns.md +183 -0
- package/rules/nestjs/configuration/SKILL.md +41 -0
- package/rules/nestjs/configuration/references/REFERENCE.md +13 -0
- package/rules/nestjs/configuration/references/config-patterns.md +184 -0
- package/rules/nestjs/controllers-services/SKILL.md +63 -0
- package/rules/nestjs/controllers-services/references/REFERENCE.md +14 -0
- package/rules/nestjs/controllers-services/references/controller-patterns.md +119 -0
- package/rules/nestjs/controllers-services/references/service-patterns.md +129 -0
- package/rules/nestjs/database/SKILL.md +102 -0
- package/rules/nestjs/database/references/REFERENCE.md +14 -0
- package/rules/nestjs/database/references/typeorm-patterns.md +156 -0
- package/rules/nestjs/deployment/SKILL.md +36 -0
- package/rules/nestjs/deployment/references/REFERENCE.md +13 -0
- package/rules/nestjs/deployment/references/deployment-patterns.md +140 -0
- package/rules/nestjs/documentation/SKILL.md +64 -0
- package/rules/nestjs/documentation/references/REFERENCE.md +13 -0
- package/rules/nestjs/documentation/references/swagger-patterns.md +139 -0
- package/rules/nestjs/error-handling/SKILL.md +55 -0
- package/rules/nestjs/error-handling/references/REFERENCE.md +13 -0
- package/rules/nestjs/error-handling/references/exception-filters.md +152 -0
- package/rules/nestjs/file-uploads/SKILL.md +35 -0
- package/rules/nestjs/file-uploads/references/REFERENCE.md +13 -0
- package/rules/nestjs/file-uploads/references/upload-patterns.md +125 -0
- package/rules/nestjs/observability/SKILL.md +39 -0
- package/rules/nestjs/observability/references/REFERENCE.md +13 -0
- package/rules/nestjs/observability/references/logging-metrics.md +175 -0
- package/rules/nestjs/performance/SKILL.md +60 -0
- package/rules/nestjs/performance/references/REFERENCE.md +13 -0
- package/rules/nestjs/performance/references/performance-patterns.md +107 -0
- package/rules/nestjs/real-time/SKILL.md +45 -0
- package/rules/nestjs/real-time/references/REFERENCE.md +13 -0
- package/rules/nestjs/real-time/references/websocket-patterns.md +121 -0
- package/rules/nestjs/scheduling/SKILL.md +39 -0
- package/rules/nestjs/scheduling/references/REFERENCE.md +13 -0
- package/rules/nestjs/scheduling/references/scheduling-patterns.md +137 -0
- package/rules/nestjs/search/SKILL.md +41 -0
- package/rules/nestjs/search/references/REFERENCE.md +13 -0
- package/rules/nestjs/search/references/search-patterns.md +137 -0
- package/rules/nestjs/security/SKILL.md +87 -0
- package/rules/nestjs/security/references/REFERENCE.md +14 -0
- package/rules/nestjs/security/references/authentication.md +151 -0
- package/rules/nestjs/testing/SKILL.md +40 -0
- package/rules/nestjs/testing/references/REFERENCE.md +14 -0
- package/rules/nestjs/testing/references/unit-testing.md +179 -0
- package/rules/nestjs/transport/SKILL.md +45 -0
- package/rules/nestjs/transport/references/REFERENCE.md +13 -0
- package/rules/nestjs/transport/references/microservices-patterns.md +170 -0
- package/rules/nextjs/app-router/SKILL.md +46 -0
- package/rules/nextjs/app-router/references/REFERENCE.md +14 -0
- package/rules/nextjs/app-router/references/routing-patterns.md +182 -0
- package/rules/nextjs/architecture/SKILL.md +44 -0
- package/rules/nextjs/architecture/references/fsd-structure.md +77 -0
- package/rules/nextjs/authentication/SKILL.md +29 -0
- package/rules/nextjs/authentication/references/auth-implementation.md +73 -0
- package/rules/nextjs/caching/SKILL.md +66 -0
- package/rules/nextjs/caching/references/REFERENCE.md +13 -0
- package/rules/nextjs/caching/references/cache-strategies.md +168 -0
- package/rules/nextjs/data-access-layer/SKILL.md +33 -0
- package/rules/nextjs/data-access-layer/references/patterns.md +66 -0
- package/rules/nextjs/data-fetching/SKILL.md +59 -0
- package/rules/nextjs/data-fetching/references/REFERENCE.md +13 -0
- package/rules/nextjs/data-fetching/references/fetch-patterns.md +160 -0
- package/rules/nextjs/internationalization/SKILL.md +105 -0
- package/rules/nextjs/internationalization/references/REFERENCE.md +13 -0
- package/rules/nextjs/internationalization/references/i18n-patterns.md +180 -0
- package/rules/nextjs/optimization/SKILL.md +64 -0
- package/rules/nextjs/optimization/references/REFERENCE.md +13 -0
- package/rules/nextjs/optimization/references/optimization-patterns.md +190 -0
- package/rules/nextjs/rendering/SKILL.md +91 -0
- package/rules/nextjs/rendering/references/REFERENCE.md +13 -0
- package/rules/nextjs/rendering/references/rendering-modes.md +163 -0
- package/rules/nextjs/server-actions/SKILL.md +46 -0
- package/rules/nextjs/server-actions/references/REFERENCE.md +13 -0
- package/rules/nextjs/server-actions/references/action-patterns.md +188 -0
- package/rules/nextjs/server-components/SKILL.md +52 -0
- package/rules/nextjs/server-components/references/REFERENCE.md +13 -0
- package/rules/nextjs/server-components/references/component-patterns.md +175 -0
- package/rules/nextjs/state-management/SKILL.md +73 -0
- package/rules/nextjs/state-management/references/REFERENCE.md +13 -0
- package/rules/nextjs/state-management/references/state-patterns.md +218 -0
- package/rules/nextjs/styling/SKILL.md +31 -0
- package/rules/nextjs/styling/references/implementation.md +56 -0
- package/rules/react/component-patterns/SKILL.md +66 -0
- package/rules/react/component-patterns/references/REFERENCE.md +126 -0
- package/rules/react/hooks/SKILL.md +60 -0
- package/rules/react/hooks/references/REFERENCE.md +132 -0
- package/rules/react/hooks.rule.md +79 -0
- package/rules/react/performance/SKILL.md +69 -0
- package/rules/react/performance/references/REFERENCE.md +143 -0
- package/rules/react/security/SKILL.md +46 -0
- package/rules/react/security/references/REFERENCE.md +170 -0
- package/rules/react/state-management/SKILL.md +56 -0
- package/rules/react/state-management/references/REFERENCE.md +137 -0
- package/rules/react/testing/SKILL.md +45 -0
- package/rules/react/testing/references/REFERENCE.md +149 -0
- package/rules/react/tooling/SKILL.md +39 -0
- package/rules/react/typescript/SKILL.md +53 -0
- package/rules/rust/actix-web/SKILL.md +160 -0
- package/rules/rust/actix-web/references/REFERENCE.md +13 -0
- package/rules/rust/actix-web/references/handler-patterns.md +198 -0
- package/rules/rust/async-graphql/SKILL.md +228 -0
- package/rules/rust/async-graphql/references/REFERENCE.md +13 -0
- package/rules/rust/async-graphql/references/schema-patterns.md +215 -0
- package/rules/rust/axum/SKILL.md +161 -0
- package/rules/rust/axum/references/REFERENCE.md +14 -0
- package/rules/rust/axum/references/handler-patterns.md +97 -0
- package/rules/rust/bevy/SKILL.md +206 -0
- package/rules/rust/bevy/references/REFERENCE.md +13 -0
- package/rules/rust/bevy/references/ecs-patterns.md +226 -0
- package/rules/rust/clap/SKILL.md +217 -0
- package/rules/rust/clap/references/REFERENCE.md +13 -0
- package/rules/rust/clap/references/derive-patterns.md +205 -0
- package/rules/rust/core/SKILL.md +154 -0
- package/rules/rust/core/references/REFERENCE.md +14 -0
- package/rules/rust/core/references/error-handling.md +92 -0
- package/rules/rust/diesel-orm/SKILL.md +176 -0
- package/rules/rust/diesel-orm/references/REFERENCE.md +13 -0
- package/rules/rust/diesel-orm/references/schema-patterns.md +206 -0
- package/rules/rust/rocket/SKILL.md +182 -0
- package/rules/rust/rocket/references/REFERENCE.md +13 -0
- package/rules/rust/rocket/references/handler-patterns.md +209 -0
- package/rules/rust/sea-orm/SKILL.md +230 -0
- package/rules/rust/sea-orm/references/REFERENCE.md +13 -0
- package/rules/rust/sea-orm/references/entity-patterns.md +221 -0
- package/rules/rust/serde-serialization/SKILL.md +150 -0
- package/rules/rust/serde-serialization/references/REFERENCE.md +13 -0
- package/rules/rust/serde-serialization/references/serialization-patterns.md +199 -0
- package/rules/rust/sqlx-database/SKILL.md +140 -0
- package/rules/rust/sqlx-database/references/REFERENCE.md +13 -0
- package/rules/rust/sqlx-database/references/query-patterns.md +210 -0
- package/rules/rust/tauri/SKILL.md +180 -0
- package/rules/rust/tauri/references/REFERENCE.md +13 -0
- package/rules/rust/tauri/references/command-patterns.md +209 -0
- package/rules/rust/tokio-runtime/SKILL.md +167 -0
- package/rules/rust/tokio-runtime/references/REFERENCE.md +14 -0
- package/rules/rust/tokio-runtime/references/async-patterns.md +137 -0
- package/rules/rust/tokio-runtime/references/synchronization.md +152 -0
- package/rules/rust/tonic/SKILL.md +231 -0
- package/rules/rust/tonic/references/REFERENCE.md +13 -0
- package/rules/rust/tonic/references/service-patterns.md +213 -0
- package/rules/rust/tracing/SKILL.md +214 -0
- package/rules/rust/tracing/references/REFERENCE.md +13 -0
- package/rules/rust/tracing/references/instrumentation.md +187 -0
- package/rules/typescript/best-practices/SKILL.md +108 -0
- package/rules/typescript/best-practices/references/REFERENCE.md +68 -0
- package/rules/typescript/language/SKILL.md +72 -0
- package/rules/typescript/language/references/REFERENCE.md +67 -0
- package/rules/typescript/patterns.rule.md +85 -0
- package/rules/typescript/security/SKILL.md +59 -0
- package/rules/typescript/security/references/REFERENCE.md +113 -0
- package/rules/typescript/tooling/SKILL.md +52 -0
- package/rules/typescript/tooling/references/REFERENCE.md +110 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# C# Language Patterns Reference
|
|
2
|
+
|
|
3
|
+
Advanced type patterns, spans, and modern C# features.
|
|
4
|
+
|
|
5
|
+
## References
|
|
6
|
+
|
|
7
|
+
- [**Pattern Matching**](pattern-matching.md) - Switch expressions, property patterns.
|
|
8
|
+
- [**Nullable Annotations**](nullable-annotations.md) - `[NotNull]`, `[MaybeNull]`, flow analysis.
|
|
9
|
+
- [**Spans & Memory**](spans-memory.md) - Zero-allocation patterns.
|
|
10
|
+
|
|
11
|
+
## Advanced Pattern Matching
|
|
12
|
+
|
|
13
|
+
```csharp
|
|
14
|
+
// List patterns (C# 11+)
|
|
15
|
+
int[] numbers = [1, 2, 3, 4, 5];
|
|
16
|
+
var result = numbers switch
|
|
17
|
+
{
|
|
18
|
+
[1, 2, ..] => "Starts with 1, 2",
|
|
19
|
+
[_, _, 3, ..] => "Third element is 3",
|
|
20
|
+
{ Length: > 10 } => "Long array",
|
|
21
|
+
[] => "Empty",
|
|
22
|
+
_ => "Other"
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Property patterns with nested matching
|
|
26
|
+
string Describe(Person person) => person switch
|
|
27
|
+
{
|
|
28
|
+
{ Name: "Admin", Role: { Permissions: { IsAdmin: true } } } => "Full access",
|
|
29
|
+
{ Age: >= 18, IsVerified: true } => "Verified adult",
|
|
30
|
+
{ Age: < 18 } => "Minor",
|
|
31
|
+
_ => "Standard user"
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Relational patterns
|
|
35
|
+
string GetTaxBracket(decimal income) => income switch
|
|
36
|
+
{
|
|
37
|
+
<= 10_000m => "0%",
|
|
38
|
+
<= 50_000m => "10%",
|
|
39
|
+
<= 100_000m => "20%",
|
|
40
|
+
_ => "30%"
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Nullable Reference Types
|
|
45
|
+
|
|
46
|
+
```csharp
|
|
47
|
+
// Nullable annotations
|
|
48
|
+
public class UserService
|
|
49
|
+
{
|
|
50
|
+
// Parameter never null after validation
|
|
51
|
+
public User GetUser([NotNull] string? id)
|
|
52
|
+
{
|
|
53
|
+
ArgumentNullException.ThrowIfNull(id);
|
|
54
|
+
return _repo.Find(id);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Return may be null
|
|
58
|
+
[return: MaybeNull]
|
|
59
|
+
public T Find<T>(int id) where T : class
|
|
60
|
+
{
|
|
61
|
+
return _context.Set<T>().Find(id);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Output parameter set if returns true
|
|
65
|
+
public bool TryGet(int id, [NotNullWhen(true)] out User? user)
|
|
66
|
+
{
|
|
67
|
+
user = _repo.Find(id);
|
|
68
|
+
return user is not null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Null-coalescing patterns
|
|
73
|
+
string name = user?.Name ?? "Anonymous";
|
|
74
|
+
int length = text?.Length ?? 0;
|
|
75
|
+
|
|
76
|
+
// Null-conditional with assignment
|
|
77
|
+
user?.Settings?.Theme = "dark"; // No-op if null
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Span and Memory Patterns
|
|
81
|
+
|
|
82
|
+
```csharp
|
|
83
|
+
// Zero-allocation string processing
|
|
84
|
+
public static int CountWords(ReadOnlySpan<char> text)
|
|
85
|
+
{
|
|
86
|
+
if (text.IsEmpty) return 0;
|
|
87
|
+
|
|
88
|
+
int count = 0;
|
|
89
|
+
bool inWord = false;
|
|
90
|
+
|
|
91
|
+
foreach (char c in text)
|
|
92
|
+
{
|
|
93
|
+
if (char.IsWhiteSpace(c))
|
|
94
|
+
{
|
|
95
|
+
inWord = false;
|
|
96
|
+
}
|
|
97
|
+
else if (!inWord)
|
|
98
|
+
{
|
|
99
|
+
inWord = true;
|
|
100
|
+
count++;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return count;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Stackalloc for small buffers
|
|
107
|
+
public static string FormatId(int id)
|
|
108
|
+
{
|
|
109
|
+
Span<char> buffer = stackalloc char[16];
|
|
110
|
+
id.TryFormat(buffer, out int written);
|
|
111
|
+
return new string(buffer[..written]);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Memory<T> for async scenarios
|
|
115
|
+
public async Task ProcessAsync(Memory<byte> buffer)
|
|
116
|
+
{
|
|
117
|
+
await _stream.ReadAsync(buffer);
|
|
118
|
+
// Process buffer...
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Generics with Constraints
|
|
123
|
+
|
|
124
|
+
```csharp
|
|
125
|
+
// Multiple constraints
|
|
126
|
+
public class Repository<T> where T : class, IEntity, new()
|
|
127
|
+
{
|
|
128
|
+
public T Create() => new T();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Covariance (out) - can return derived types
|
|
132
|
+
public interface IReadOnlyRepository<out T>
|
|
133
|
+
{
|
|
134
|
+
T? GetById(int id);
|
|
135
|
+
IEnumerable<T> GetAll();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Contravariance (in) - can accept base types
|
|
139
|
+
public interface IComparer<in T>
|
|
140
|
+
{
|
|
141
|
+
int Compare(T x, T y);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Static abstract members (C# 11+)
|
|
145
|
+
public interface IParsable<TSelf> where TSelf : IParsable<TSelf>
|
|
146
|
+
{
|
|
147
|
+
static abstract TSelf Parse(string s);
|
|
148
|
+
static abstract bool TryParse(string? s, out TSelf result);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## C# 12+ Features
|
|
153
|
+
|
|
154
|
+
```csharp
|
|
155
|
+
// Collection expressions
|
|
156
|
+
int[] numbers = [1, 2, 3, 4, 5];
|
|
157
|
+
List<string> names = ["Alice", "Bob"];
|
|
158
|
+
Span<int> span = [1, 2, 3];
|
|
159
|
+
|
|
160
|
+
// Spread operator
|
|
161
|
+
int[] combined = [..numbers, 6, 7, ..otherNumbers];
|
|
162
|
+
|
|
163
|
+
// Primary constructors for classes
|
|
164
|
+
public class OrderService(IOrderRepository repo, ILogger<OrderService> logger)
|
|
165
|
+
{
|
|
166
|
+
public async Task<Order?> GetAsync(int id)
|
|
167
|
+
{
|
|
168
|
+
logger.LogDebug("Fetching order {OrderId}", id);
|
|
169
|
+
return await repo.GetByIdAsync(id);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Alias any type
|
|
174
|
+
using Point = (int X, int Y);
|
|
175
|
+
using UserId = System.Int32;
|
|
176
|
+
|
|
177
|
+
// Default lambda parameters
|
|
178
|
+
var greet = (string name = "World") => $"Hello, {name}!";
|
|
179
|
+
|
|
180
|
+
// Inline arrays (for high-performance scenarios)
|
|
181
|
+
[InlineArray(16)]
|
|
182
|
+
public struct Buffer16<T>
|
|
183
|
+
{
|
|
184
|
+
private T _element0;
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Async Patterns
|
|
189
|
+
|
|
190
|
+
```csharp
|
|
191
|
+
// ValueTask for hot paths
|
|
192
|
+
public ValueTask<int> GetCachedCountAsync()
|
|
193
|
+
{
|
|
194
|
+
if (_cache.TryGetValue("count", out int count))
|
|
195
|
+
return ValueTask.FromResult(count);
|
|
196
|
+
|
|
197
|
+
return new ValueTask<int>(LoadCountAsync());
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// IAsyncEnumerable for streaming
|
|
201
|
+
public async IAsyncEnumerable<User> GetUsersAsync(
|
|
202
|
+
[EnumeratorCancellation] CancellationToken ct = default)
|
|
203
|
+
{
|
|
204
|
+
await foreach (var user in _db.Users.AsAsyncEnumerable().WithCancellation(ct))
|
|
205
|
+
{
|
|
206
|
+
yield return user;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Parallel async with SemaphoreSlim
|
|
211
|
+
public async Task ProcessAllAsync(IEnumerable<int> ids, int maxConcurrency = 10)
|
|
212
|
+
{
|
|
213
|
+
using var semaphore = new SemaphoreSlim(maxConcurrency);
|
|
214
|
+
var tasks = ids.Select(async id =>
|
|
215
|
+
{
|
|
216
|
+
await semaphore.WaitAsync();
|
|
217
|
+
try { await ProcessAsync(id); }
|
|
218
|
+
finally { semaphore.Release(); }
|
|
219
|
+
});
|
|
220
|
+
await Task.WhenAll(tasks);
|
|
221
|
+
}
|
|
222
|
+
```
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: dotnet-patterns
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
triggers:
|
|
5
|
+
files: ['**/*.cs']
|
|
6
|
+
keywords: [Result, Repository, CQRS, MediatR, Handler, Command, Query]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# .NET Design Patterns
|
|
10
|
+
|
|
11
|
+
Common architectural and design patterns for .NET applications.
|
|
12
|
+
|
|
13
|
+
## Result Pattern
|
|
14
|
+
|
|
15
|
+
Explicit success/failure handling without exceptions for expected failures.
|
|
16
|
+
|
|
17
|
+
```csharp
|
|
18
|
+
public readonly struct Result<T>
|
|
19
|
+
{
|
|
20
|
+
public bool IsSuccess { get; }
|
|
21
|
+
public T? Value { get; }
|
|
22
|
+
public string? Error { get; }
|
|
23
|
+
|
|
24
|
+
private Result(T value) => (IsSuccess, Value) = (true, value);
|
|
25
|
+
private Result(string error) => (IsSuccess, Error) = (false, error);
|
|
26
|
+
|
|
27
|
+
public static Result<T> Success(T value) => new(value);
|
|
28
|
+
public static Result<T> Failure(string error) => new(error);
|
|
29
|
+
|
|
30
|
+
public TResult Match<TResult>(
|
|
31
|
+
Func<T, TResult> onSuccess,
|
|
32
|
+
Func<string, TResult> onFailure) =>
|
|
33
|
+
IsSuccess ? onSuccess(Value!) : onFailure(Error!);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Usage
|
|
37
|
+
public async Task<Result<User>> GetUserAsync(int id)
|
|
38
|
+
{
|
|
39
|
+
var user = await _repo.FindAsync(id);
|
|
40
|
+
return user is null
|
|
41
|
+
? Result<User>.Failure("User not found")
|
|
42
|
+
: Result<User>.Success(user);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// In controller
|
|
46
|
+
var result = await _service.GetUserAsync(id);
|
|
47
|
+
return result.Match<IActionResult>(
|
|
48
|
+
user => Ok(user),
|
|
49
|
+
error => NotFound(new { error }));
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Repository Pattern
|
|
53
|
+
|
|
54
|
+
Abstract data access with a generic interface.
|
|
55
|
+
|
|
56
|
+
```csharp
|
|
57
|
+
public interface IRepository<T> where T : class, IEntity
|
|
58
|
+
{
|
|
59
|
+
Task<T?> GetByIdAsync(int id, CancellationToken ct = default);
|
|
60
|
+
Task<IReadOnlyList<T>> GetAllAsync(CancellationToken ct = default);
|
|
61
|
+
Task<T> AddAsync(T entity, CancellationToken ct = default);
|
|
62
|
+
Task UpdateAsync(T entity, CancellationToken ct = default);
|
|
63
|
+
Task DeleteAsync(T entity, CancellationToken ct = default);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public class Repository<T>(AppDbContext context) : IRepository<T>
|
|
67
|
+
where T : class, IEntity
|
|
68
|
+
{
|
|
69
|
+
protected readonly DbSet<T> DbSet = context.Set<T>();
|
|
70
|
+
|
|
71
|
+
public virtual async Task<T?> GetByIdAsync(int id, CancellationToken ct = default) =>
|
|
72
|
+
await DbSet.FindAsync([id], ct);
|
|
73
|
+
|
|
74
|
+
public virtual async Task<IReadOnlyList<T>> GetAllAsync(CancellationToken ct = default) =>
|
|
75
|
+
await DbSet.ToListAsync(ct);
|
|
76
|
+
|
|
77
|
+
public virtual async Task<T> AddAsync(T entity, CancellationToken ct = default)
|
|
78
|
+
{
|
|
79
|
+
await DbSet.AddAsync(entity, ct);
|
|
80
|
+
await context.SaveChangesAsync(ct);
|
|
81
|
+
return entity;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public virtual async Task UpdateAsync(T entity, CancellationToken ct = default)
|
|
85
|
+
{
|
|
86
|
+
DbSet.Update(entity);
|
|
87
|
+
await context.SaveChangesAsync(ct);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public virtual async Task DeleteAsync(T entity, CancellationToken ct = default)
|
|
91
|
+
{
|
|
92
|
+
DbSet.Remove(entity);
|
|
93
|
+
await context.SaveChangesAsync(ct);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## CQRS with MediatR
|
|
99
|
+
|
|
100
|
+
Separate read (Query) and write (Command) operations.
|
|
101
|
+
|
|
102
|
+
```csharp
|
|
103
|
+
// Command
|
|
104
|
+
public record CreateOrderCommand(int UserId, List<OrderItemDto> Items)
|
|
105
|
+
: IRequest<Result<int>>;
|
|
106
|
+
|
|
107
|
+
public class CreateOrderHandler(
|
|
108
|
+
IOrderRepository repo,
|
|
109
|
+
IUnitOfWork uow,
|
|
110
|
+
ILogger<CreateOrderHandler> logger)
|
|
111
|
+
: IRequestHandler<CreateOrderCommand, Result<int>>
|
|
112
|
+
{
|
|
113
|
+
public async Task<Result<int>> Handle(
|
|
114
|
+
CreateOrderCommand request,
|
|
115
|
+
CancellationToken ct)
|
|
116
|
+
{
|
|
117
|
+
var order = Order.Create(request.UserId, request.Items);
|
|
118
|
+
|
|
119
|
+
if (order.IsFailure)
|
|
120
|
+
return Result<int>.Failure(order.Error!);
|
|
121
|
+
|
|
122
|
+
await repo.AddAsync(order.Value!, ct);
|
|
123
|
+
await uow.SaveChangesAsync(ct);
|
|
124
|
+
|
|
125
|
+
logger.LogInformation("Order {OrderId} created", order.Value!.Id);
|
|
126
|
+
|
|
127
|
+
return Result<int>.Success(order.Value!.Id);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Query
|
|
132
|
+
public record GetOrderQuery(int OrderId) : IRequest<OrderDto?>;
|
|
133
|
+
|
|
134
|
+
public class GetOrderHandler(IOrderRepository repo)
|
|
135
|
+
: IRequestHandler<GetOrderQuery, OrderDto?>
|
|
136
|
+
{
|
|
137
|
+
public async Task<OrderDto?> Handle(GetOrderQuery request, CancellationToken ct) =>
|
|
138
|
+
await repo.GetDtoByIdAsync(request.OrderId, ct);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Controller usage
|
|
142
|
+
[ApiController]
|
|
143
|
+
[Route("api/orders")]
|
|
144
|
+
public class OrdersController(ISender mediator) : ControllerBase
|
|
145
|
+
{
|
|
146
|
+
[HttpPost]
|
|
147
|
+
public async Task<IActionResult> Create(CreateOrderRequest request)
|
|
148
|
+
{
|
|
149
|
+
var command = new CreateOrderCommand(request.UserId, request.Items);
|
|
150
|
+
var result = await mediator.Send(command);
|
|
151
|
+
|
|
152
|
+
return result.Match<IActionResult>(
|
|
153
|
+
id => CreatedAtAction(nameof(Get), new { id }, null),
|
|
154
|
+
error => BadRequest(new { error }));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
[HttpGet("{id:int}")]
|
|
158
|
+
public async Task<IActionResult> Get(int id)
|
|
159
|
+
{
|
|
160
|
+
var order = await mediator.Send(new GetOrderQuery(id));
|
|
161
|
+
return order is null ? NotFound() : Ok(order);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Options Pattern
|
|
167
|
+
|
|
168
|
+
Strongly-typed configuration with validation.
|
|
169
|
+
|
|
170
|
+
```csharp
|
|
171
|
+
public class EmailSettings
|
|
172
|
+
{
|
|
173
|
+
public const string SectionName = "Email";
|
|
174
|
+
|
|
175
|
+
[Required]
|
|
176
|
+
public string SmtpHost { get; init; } = string.Empty;
|
|
177
|
+
|
|
178
|
+
[Range(1, 65535)]
|
|
179
|
+
public int SmtpPort { get; init; } = 587;
|
|
180
|
+
|
|
181
|
+
[Required, EmailAddress]
|
|
182
|
+
public string FromAddress { get; init; } = string.Empty;
|
|
183
|
+
|
|
184
|
+
public bool UseSsl { get; init; } = true;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Registration with validation
|
|
188
|
+
builder.Services.AddOptions<EmailSettings>()
|
|
189
|
+
.BindConfiguration(EmailSettings.SectionName)
|
|
190
|
+
.ValidateDataAnnotations()
|
|
191
|
+
.ValidateOnStart();
|
|
192
|
+
|
|
193
|
+
// Usage
|
|
194
|
+
public class EmailService(IOptions<EmailSettings> options)
|
|
195
|
+
{
|
|
196
|
+
private readonly EmailSettings _settings = options.Value;
|
|
197
|
+
|
|
198
|
+
public async Task SendAsync(string to, string subject, string body)
|
|
199
|
+
{
|
|
200
|
+
using var client = new SmtpClient(_settings.SmtpHost, _settings.SmtpPort);
|
|
201
|
+
client.EnableSsl = _settings.UseSsl;
|
|
202
|
+
// Send email...
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Specification Pattern
|
|
208
|
+
|
|
209
|
+
Encapsulate query logic for reuse and composition.
|
|
210
|
+
|
|
211
|
+
```csharp
|
|
212
|
+
public abstract class Specification<T>
|
|
213
|
+
{
|
|
214
|
+
public abstract Expression<Func<T, bool>> ToExpression();
|
|
215
|
+
|
|
216
|
+
public bool IsSatisfiedBy(T entity) =>
|
|
217
|
+
ToExpression().Compile()(entity);
|
|
218
|
+
|
|
219
|
+
public Specification<T> And(Specification<T> other) =>
|
|
220
|
+
new AndSpecification<T>(this, other);
|
|
221
|
+
|
|
222
|
+
public Specification<T> Or(Specification<T> other) =>
|
|
223
|
+
new OrSpecification<T>(this, other);
|
|
224
|
+
|
|
225
|
+
public Specification<T> Not() =>
|
|
226
|
+
new NotSpecification<T>(this);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
public class AndSpecification<T>(Specification<T> left, Specification<T> right)
|
|
230
|
+
: Specification<T>
|
|
231
|
+
{
|
|
232
|
+
public override Expression<Func<T, bool>> ToExpression()
|
|
233
|
+
{
|
|
234
|
+
var leftExpr = left.ToExpression();
|
|
235
|
+
var rightExpr = right.ToExpression();
|
|
236
|
+
|
|
237
|
+
var param = Expression.Parameter(typeof(T));
|
|
238
|
+
var body = Expression.AndAlso(
|
|
239
|
+
Expression.Invoke(leftExpr, param),
|
|
240
|
+
Expression.Invoke(rightExpr, param));
|
|
241
|
+
|
|
242
|
+
return Expression.Lambda<Func<T, bool>>(body, param);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Concrete specifications
|
|
247
|
+
public class ActiveUserSpec : Specification<User>
|
|
248
|
+
{
|
|
249
|
+
public override Expression<Func<User, bool>> ToExpression() =>
|
|
250
|
+
user => user.IsActive && !user.IsDeleted;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
public class UserInRoleSpec(string role) : Specification<User>
|
|
254
|
+
{
|
|
255
|
+
public override Expression<Func<User, bool>> ToExpression() =>
|
|
256
|
+
user => user.Role == role;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Usage
|
|
260
|
+
var spec = new ActiveUserSpec().And(new UserInRoleSpec("Admin"));
|
|
261
|
+
var admins = await _context.Users.Where(spec.ToExpression()).ToListAsync();
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Unit of Work
|
|
265
|
+
|
|
266
|
+
Coordinate multiple repositories in a transaction.
|
|
267
|
+
|
|
268
|
+
```csharp
|
|
269
|
+
public interface IUnitOfWork : IAsyncDisposable
|
|
270
|
+
{
|
|
271
|
+
IUserRepository Users { get; }
|
|
272
|
+
IOrderRepository Orders { get; }
|
|
273
|
+
IProductRepository Products { get; }
|
|
274
|
+
|
|
275
|
+
Task<int> SaveChangesAsync(CancellationToken ct = default);
|
|
276
|
+
Task BeginTransactionAsync(CancellationToken ct = default);
|
|
277
|
+
Task CommitAsync(CancellationToken ct = default);
|
|
278
|
+
Task RollbackAsync(CancellationToken ct = default);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
public class UnitOfWork(AppDbContext context) : IUnitOfWork
|
|
282
|
+
{
|
|
283
|
+
private IDbContextTransaction? _transaction;
|
|
284
|
+
private IUserRepository? _users;
|
|
285
|
+
private IOrderRepository? _orders;
|
|
286
|
+
private IProductRepository? _products;
|
|
287
|
+
|
|
288
|
+
public IUserRepository Users => _users ??= new UserRepository(context);
|
|
289
|
+
public IOrderRepository Orders => _orders ??= new OrderRepository(context);
|
|
290
|
+
public IProductRepository Products => _products ??= new ProductRepository(context);
|
|
291
|
+
|
|
292
|
+
public Task<int> SaveChangesAsync(CancellationToken ct = default) =>
|
|
293
|
+
context.SaveChangesAsync(ct);
|
|
294
|
+
|
|
295
|
+
public async Task BeginTransactionAsync(CancellationToken ct = default) =>
|
|
296
|
+
_transaction = await context.Database.BeginTransactionAsync(ct);
|
|
297
|
+
|
|
298
|
+
public async Task CommitAsync(CancellationToken ct = default)
|
|
299
|
+
{
|
|
300
|
+
await context.SaveChangesAsync(ct);
|
|
301
|
+
if (_transaction is not null)
|
|
302
|
+
await _transaction.CommitAsync(ct);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
public async Task RollbackAsync(CancellationToken ct = default)
|
|
306
|
+
{
|
|
307
|
+
if (_transaction is not null)
|
|
308
|
+
await _transaction.RollbackAsync(ct);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
public async ValueTask DisposeAsync()
|
|
312
|
+
{
|
|
313
|
+
if (_transaction is not null)
|
|
314
|
+
await _transaction.DisposeAsync();
|
|
315
|
+
await context.DisposeAsync();
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Usage
|
|
320
|
+
public class OrderService(IUnitOfWork uow)
|
|
321
|
+
{
|
|
322
|
+
public async Task<Result<int>> CreateOrderWithInventoryUpdate(
|
|
323
|
+
CreateOrderCommand command, CancellationToken ct)
|
|
324
|
+
{
|
|
325
|
+
await uow.BeginTransactionAsync(ct);
|
|
326
|
+
|
|
327
|
+
try
|
|
328
|
+
{
|
|
329
|
+
var order = new Order(command.UserId);
|
|
330
|
+
|
|
331
|
+
foreach (var item in command.Items)
|
|
332
|
+
{
|
|
333
|
+
var product = await uow.Products.GetByIdAsync(item.ProductId, ct);
|
|
334
|
+
if (product is null || product.Stock < item.Quantity)
|
|
335
|
+
{
|
|
336
|
+
await uow.RollbackAsync(ct);
|
|
337
|
+
return Result<int>.Failure("Insufficient stock");
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
product.Stock -= item.Quantity;
|
|
341
|
+
order.AddItem(product, item.Quantity);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
await uow.Orders.AddAsync(order, ct);
|
|
345
|
+
await uow.CommitAsync(ct);
|
|
346
|
+
|
|
347
|
+
return Result<int>.Success(order.Id);
|
|
348
|
+
}
|
|
349
|
+
catch
|
|
350
|
+
{
|
|
351
|
+
await uow.RollbackAsync(ct);
|
|
352
|
+
throw;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## Factory Pattern
|
|
359
|
+
|
|
360
|
+
Encapsulate object creation logic.
|
|
361
|
+
|
|
362
|
+
```csharp
|
|
363
|
+
public interface INotificationFactory
|
|
364
|
+
{
|
|
365
|
+
INotification Create(NotificationType type);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
public class NotificationFactory(IServiceProvider services) : INotificationFactory
|
|
369
|
+
{
|
|
370
|
+
public INotification Create(NotificationType type) => type switch
|
|
371
|
+
{
|
|
372
|
+
NotificationType.Email => services.GetRequiredService<EmailNotification>(),
|
|
373
|
+
NotificationType.Sms => services.GetRequiredService<SmsNotification>(),
|
|
374
|
+
NotificationType.Push => services.GetRequiredService<PushNotification>(),
|
|
375
|
+
_ => throw new ArgumentException($"Unknown notification type: {type}")
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Registration
|
|
380
|
+
builder.Services.AddTransient<EmailNotification>();
|
|
381
|
+
builder.Services.AddTransient<SmsNotification>();
|
|
382
|
+
builder.Services.AddTransient<PushNotification>();
|
|
383
|
+
builder.Services.AddSingleton<INotificationFactory, NotificationFactory>();
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## Related Topics
|
|
387
|
+
|
|
388
|
+
See also: [language](language/SKILL.md) | [best-practices](best-practices/SKILL.md) | [aspnet-core](aspnet-core/SKILL.md)
|