# Shani.AttributeInjection **Repository Path**: gavinw/shani.-attribute-injection ## Basic Information - **Project Name**: Shani.AttributeInjection - **Description**: Provide the attribute-based registration mechanism for asp.net core - **Primary Language**: C# - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-11-09 - **Last Updated**: 2025-12-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Shani.AttributeInjection Shani.AttributeInjection is a lightweight utility for ASP.NET Core that enables attribute-based service registration and convenient service resolution patterns. It provides: - Attribute-driven registration via `[Injectable]`. - A small set of helpers for resolving services (`Inject`, `ServiceInjector`, and the unified `InjectableBase`). - Support for scoped service sharing across nested operations using an ambient/shared scope. This project is designed to make it easier to register and consume DI services in small apps, tools, or migration scenarios where constructor injection is not always practical. ## Features - Automatic discovery and registration of types marked with `[Injectable]`. - Attribute options to control the service lifetime and service type. - `InjectableBase` unified base that supports global (root) resolution, owned scopes, and shared (ambient) scopes. - Ambient shared-scope support (`BeginSharedScope`) to allow nested scoped classes to share the same `IServiceScope`. - Small helpers for quick service access (`ServiceInjector`, `Inject`). ## Getting started Prerequisites - .NET SDK 8.0 or later Quick setup (example) 1. Register services (automatic discovery) in your startup code: ```csharp var builder = WebApplication.CreateBuilder(args); // Discover and register types marked with [Injectable] in specified assemblies builder.Services.AddInjectableServices(typeof(Program).Assembly); var app = builder.Build(); // Make the built service provider available to the attribute-based helpers AttributeInjectionContainer.SetServiceProvider(app.Services); ``` 2. Mark services with the attribute: ```csharp [Injectable] // defaults to ServiceLifetime.Scoped public class MyService { } [Injectable(typeof(IMyInterface), ServiceLifetime.Singleton)] public class MyImpl : IMyInterface { } ``` 3. Resolve services: - Use constructor injection as usual (recommended). - Or use `InjectableBase`/`ScopedInjectableBase` to access `GetService()` / `GetRequiredService()` from a base class. - For quick static access use `ServiceInjector.GetService()` or `Inject.GetRequiredService()`. ## Ambient/shared scoped operations When you have an operation that composes multiple scoped objects (for example a parent that creates child scoped objects), you may want all of them to share the same scoped services (e.g., a single DbContext instance). This project provides an ambient/shared-scope mechanism: ```csharp using (AttributeInjectionContainer.BeginSharedScope("operation-123")) { var parent = new ParentScopedService(); // derives from ScopedInjectableBase parent.DoWork(); // child scoped objects created inside will join the same scope } ``` The ambient scope flows with async/await (uses AsyncLocal) and is reference-counted. Use `BeginSharedScope` to create the ambient scope and ensure it is disposed when the top-level operation completes. There is also `AttributeInjectionContainer.AcquireSharedScope(key)` and `ReleaseSharedScope(key)` for manual control. ## Example: SampleSharedScopeUsage The `Shani.AttributeInjection.Services` project includes `SampleSharedScopeUsage.Run()` which demonstrates: - Building a minimal `ServiceCollection`. - Calling `AttributeInjectionContainer.SetServiceProvider`. - Using `BeginSharedScope` and showing parent/child scoped classes resolving the same scoped service instance. Call it from any entry point for a quick demo: ```csharp Shani.AttributeInjection.Services.SampleSharedScopeUsage.Run(); ``` ## API Overview - `InjectableAttribute` — annotate classes to control registration (service type + lifetime). - `AttributeInjectionContainer` — discovery/registration helpers, service provider access, and shared-scope APIs. - `InjectableBase` — unified base to resolve services from root/owned/shared scopes. Implements `IDisposable` when it owns or participates in scopes. - `ScopedInjectableBase` — convenience base that creates and owns a scope by default (backwards-compatible behavior). - `ServiceInjector` — static helper for quick service resolution and RunInScope helper. ## Build & run Build the solution: ```powershell dotnet build "Shani.AttributeInjection.sln" -c Debug ``` Run the sample web project (Services): ```powershell cd Shani.AttributeInjection.Services dotnet run ``` Or execute the sample runner by calling `SampleSharedScopeUsage.Run()` from a console app or `Program.cs`. ## Contributing Contributions are welcome. Suggested workflow: 1. Fork the repository 2. Create a feature branch (e.g. `feat/my-change`) 3. Add tests and documentation for changes 4. Open a pull request and describe the change ## Notes & recommendations - The ambient scope uses string keys by default. For stricter typing you can replace the key with a token type (GUID/struct/enum). - Shared-scope storage is protected by a lock to ensure thread-safety; if you expect very high concurrency we can replace with a lock-free approach. - The project intentionally provides both constructor injection and helper-based resolution. Prefer constructor injection where possible for testability. ## License This repository includes a `LICENSE` file. Follow the project license for permitted usage and contributions.