{"id":16989931,"url":"https://github.com/x39/x39.util.dependencyinjection","last_synced_at":"2026-05-10T05:17:32.473Z","repository":{"id":74149812,"uuid":"582184115","full_name":"X39/X39.Util.DependencyInjection","owner":"X39","description":null,"archived":false,"fork":false,"pushed_at":"2024-07-05T14:12:00.000Z","size":36,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-02T22:42:51.722Z","etag":null,"topics":["attributes","csharp","dependency-injection","dotnet","foss","library","nuget","reflection"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/X39.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-26T02:23:00.000Z","updated_at":"2024-07-05T14:12:04.000Z","dependencies_parsed_at":null,"dependency_job_id":"2d4e819e-3823-434f-96e6-946e246fe815","html_url":"https://github.com/X39/X39.Util.DependencyInjection","commit_stats":null,"previous_names":["x39/cs-x39-util-dependencyinjection"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/X39%2FX39.Util.DependencyInjection","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/X39%2FX39.Util.DependencyInjection/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/X39%2FX39.Util.DependencyInjection/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/X39%2FX39.Util.DependencyInjection/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/X39","download_url":"https://codeload.github.com/X39/X39.Util.DependencyInjection/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244898458,"owners_count":20528341,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["attributes","csharp","dependency-injection","dotnet","foss","library","nuget","reflection"],"created_at":"2024-10-14T03:08:28.771Z","updated_at":"2026-05-10T05:17:32.465Z","avatar_url":"https://github.com/X39.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# X39.Util.DependencyInjection\n\nAttribute-based service registration for `Microsoft.Extensions.DependencyInjection` with\ncompile-time source generation and NativeAOT support.\n\n[![NuGet](https://img.shields.io/nuget/v/X39.Util.DependencyInjection)](https://www.nuget.org/packages/X39.Util.DependencyInjection)\n\n\u003c!-- TOC --\u003e\n* [X39.Util.DependencyInjection](#x39utildependencyinjection)\n  * [Installation](#installation)\n  * [Quick Start](#quick-start)\n  * [Source Generator](#source-generator)\n    * [How It Works](#how-it-works)\n    * [Generated Code Example](#generated-code-example)\n    * [NativeAOT Support](#nativeaot-support)\n    * [Customizing the Generated Class](#customizing-the-generated-class)\n    * [Compile-Time Diagnostics](#compile-time-diagnostics)\n  * [Lifetime Attributes](#lifetime-attributes)\n  * [Conditional Registration](#conditional-registration)\n  * [Reflection-Based Registration (Legacy)](#reflection-based-registration-legacy)\n      * [`AddAttributedServicesOf(IConfiguration, Assembly)`](#addattributedservicesoficonfiguration-assembly)\n      * [`AddAttributedServicesFromAssemblyOf\u003cT\u003e(IConfiguration)`](#addattributedservicesfromassemblyofticonfiguration)\n      * [`AddAttributedServicesOf(IConfiguration, AppDomain)`](#addattributedservicesoficonfiguration-appdomain)\n  * [Behavior Notes](#behavior-notes)\n  * [Exceptions](#exceptions)\n  * [Semantic Versioning](#semantic-versioning)\n  * [Contributing](#contributing)\n    * [Code of Conduct](#code-of-conduct)\n    * [Contributor License Agreement](#contributor-license-agreement)\n  * [License](#license)\n\u003c!-- TOC --\u003e\n\n## Installation\n\n```shell\ndotnet add package X39.Util.DependencyInjection\n```\n\nOr add directly to your `.csproj`:\n\n```xml\n\u003cPackageReference Include=\"X39.Util.DependencyInjection\" Version=\"*\" /\u003e\n```\n\nThe package includes a Roslyn source generator that runs automatically at compile time.\nNo additional packages are required.\n\n## Quick Start\n\nDecorate your service class with a lifetime attribute and call the generated `AddDependencies`\nmethod during startup:\n\n```csharp\nusing X39.Util.DependencyInjection;\n\nIHost host = Host.CreateDefaultBuilder(args)\n    .ConfigureServices((context, services) =\u003e\n    {\n        services.AddDependencies(context.Configuration);\n    })\n    .Build();\n\nhost.Run();\n```\n\n```csharp\nusing X39.Util.DependencyInjection.Attributes;\n\npublic interface IMyService\n{\n    void DoWork();\n}\n\n[Singleton\u003cMyService, IMyService\u003e]\npublic class MyService : IMyService\n{\n    public void DoWork() { /* ... */ }\n}\n```\n\nThe source generator discovers all attributed classes at compile time and generates the\n`AddDependencies` extension method with explicit registration calls — no runtime reflection needed.\n\n## Source Generator\n\n### How It Works\n\nAt compile time, the included Roslyn incremental source generator:\n\n1. Scans your project for classes decorated with `[Singleton]`, `[Transient]`, or `[Scoped]` attributes.\n2. Validates attribute usage and condition methods, reporting errors as compiler diagnostics.\n3. Emits a static extension method (default: `Dependencies.AddDependencies()`) containing all\n   service registrations as direct `IServiceCollection` calls.\n\nThe generated code is fully AOT-safe — all types are statically known and no reflection is used at\nruntime.\n\n### Generated Code Example\n\nFor a class like:\n\n```csharp\n[Singleton\u003cMyService, IMyService\u003e]\npublic class MyService : IMyService\n{\n    [DependencyInjectionCondition]\n    internal static bool IsEnabled(IConfiguration configuration)\n        =\u003e configuration.GetValue\u003cbool\u003e(\"Features:MyService\");\n}\n```\n\nThe generator produces:\n\n```csharp\n// \u003cauto-generated/\u003e\npublic static class Dependencies\n{\n    public static IServiceCollection AddDependencies(\n        this IServiceCollection services,\n        IConfiguration configuration)\n    {\n        RuntimeHelpers.RunClassConstructor(typeof(MyApp.MyService).TypeHandle);\n        if (MyApp.MyService.IsEnabled(configuration))\n        {\n            services.AddSingleton\u003cMyApp.IMyService, MyApp.MyService\u003e();\n        }\n\n        return services;\n    }\n}\n```\n\n### NativeAOT Support\n\nBecause the source generator resolves all registrations at compile time, the generated code is\nfully compatible with NativeAOT publishing. There is no runtime reflection, no dynamic assembly\nscanning, and all types are explicitly referenced in the generated output.\n\n### Customizing the Generated Class\n\nBy default, the generator creates a class named `Dependencies` in the\n`X39.Util.DependencyInjection` namespace with an `AddDependencies` extension method. You can\ncustomize both via MSBuild properties in your `.csproj`:\n\n```xml\n\u003cPropertyGroup\u003e\n  \u003cX39_DependencyInjection_ClassName\u003eMyServices\u003c/X39_DependencyInjection_ClassName\u003e\n  \u003cX39_DependencyInjection_Namespace\u003eMyApp.DI\u003c/X39_DependencyInjection_Namespace\u003e\n\u003c/PropertyGroup\u003e\n```\n\nThis produces `MyApp.DI.MyServices.AddMyServices(...)` instead. The method name is always\n`Add` + the class name.\n\n### Compile-Time Diagnostics\n\nThe source generator reports errors at build time rather than at runtime:\n\n| ID         | Description                                                                                                                              |\n|------------|------------------------------------------------------------------------------------------------------------------------------------------|\n| `X39DI001` | Condition method is private. Change to `internal` or `public` for source-generated registration.                                         |\n| `X39DI002` | Multiple dependency injection attributes on the same class.                                                                              |\n| `X39DI003` | Invalid condition method signature — must be `static`, return `bool`, and accept zero parameters or a single `IConfiguration` parameter. |\n\n## Lifetime Attributes\n\nEach attribute corresponds to a standard DI lifetime. The generic forms require .NET 7+.\n\n| Attribute                             | Lifetime  | Equivalent call                          |\n|---------------------------------------|-----------|------------------------------------------|\n| `[Singleton\u003cTService\u003e]`               | Singleton | `AddSingleton\u003cTService\u003e()`               |\n| `[Singleton\u003cTService, TAbstraction\u003e]` | Singleton | `AddSingleton\u003cTAbstraction, TService\u003e()` |\n| `[Transient\u003cTService\u003e]`               | Transient | `AddTransient\u003cTService\u003e()`               |\n| `[Transient\u003cTService, TAbstraction\u003e]` | Transient | `AddTransient\u003cTAbstraction, TService\u003e()` |\n| `[Scoped\u003cTService\u003e]`                  | Scoped    | `AddScoped\u003cTService\u003e()`                  |\n| `[Scoped\u003cTService, TAbstraction\u003e]`    | Scoped    | `AddScoped\u003cTAbstraction, TService\u003e()`    |\n\nIn the two-type-parameter form, `TService` is the implementation class and `TAbstraction` is the\ninterface or base class (`TService : TAbstraction`).\n\n**Pre-.NET 7:** Non-generic versions are available using `typeof(...)`. These are marked\n`[Obsolete]` on .NET 7+ in favor of the generic forms.\n\n```csharp\n// Without abstraction (registers as itself)\n[Singleton(typeof(MyService))]\n\n// With abstraction (note: parameter order is serviceType, actualType)\n[Singleton(typeof(IMyService), typeof(MyService))]\n```\n\n## Conditional Registration\n\nUse `[DependencyInjectionCondition]` on a static method to control whether a service is registered.\nThe method must be `static`, return `bool`, and accept either no parameters or a single\n`IConfiguration` parameter.\n\n**Important:** Condition methods must be `internal` or `public` when using the source generator.\nPrivate condition methods produce a compile-time error (`X39DI001`).\n\n```csharp\npublic interface IMyService\n{\n    bool SomeFunc();\n}\n\n[Singleton\u003cDebugService, IMyService\u003e]\npublic class DebugService : IMyService\n{\n    [DependencyInjectionCondition]\n    internal static bool Condition()\n    {\n        #if DEBUG\n        return true;\n        #else\n        return false;\n        #endif\n    }\n\n    public bool SomeFunc() =\u003e true;\n}\n\n[Singleton\u003cReleaseService, IMyService\u003e]\npublic class ReleaseService : IMyService\n{\n    [DependencyInjectionCondition]\n    internal static bool Condition()\n    {\n        #if DEBUG\n        return false;\n        #else\n        return true;\n        #endif\n    }\n\n    public bool SomeFunc() =\u003e true;\n}\n```\n\nA condition method can also accept `IConfiguration` to make decisions based on app configuration:\n\n```csharp\n[DependencyInjectionCondition]\ninternal static bool IsEnabled(IConfiguration configuration)\n{\n    return configuration.GetValue\u003cbool\u003e(\"Features:MyService\");\n}\n```\n\nIf a class has multiple condition methods, **all** must return `true` for the service to be\nregistered (AND logic).\n\n## Reflection-Based Registration (Legacy)\n\nThe library also includes reflection-based registration methods that scan assemblies at runtime.\nThese are useful when source generation is not available or when scanning assemblies outside your\nproject.\n\nAll methods are extension methods on `IServiceCollection`.\n\n#### `AddAttributedServicesOf(IConfiguration, Assembly)`\n\nScans the given assembly for classes decorated with lifetime attributes and registers them.\n\n```csharp\nservices.AddAttributedServicesOf(configuration, typeof(Program).Assembly);\n```\n\n#### `AddAttributedServicesFromAssemblyOf\u003cT\u003e(IConfiguration)`\n\nConvenience overload that scans the assembly containing type `T`.\n\n```csharp\nservices.AddAttributedServicesFromAssemblyOf\u003cProgram\u003e(configuration);\n```\n\n#### `AddAttributedServicesOf(IConfiguration, AppDomain)`\n\nScans all assemblies loaded in the given `AppDomain`.\n\n```csharp\nservices.AddAttributedServicesOf(configuration, AppDomain.CurrentDomain);\n```\n\n\u003e **Note:** The reflection-based methods use runtime type scanning and are not compatible with\n\u003e NativeAOT. Prefer the source-generated `AddDependencies` method for new projects.\n\n## Behavior Notes\n\n- **Static constructors** are executed during registration (before condition methods are evaluated).\n- Only **one** lifetime attribute is allowed per class. The source generator reports `X39DI002` at\n  compile time; the reflection-based path throws `MultipleDependencyInjectionAttributesPresentException`.\n- Lifetime attributes are **not inherited** (`Inherited = false`).\n\n## Exceptions\n\nExceptions are thrown by the reflection-based registration path. The source generator reports\nequivalent issues as compile-time diagnostics instead.\n\nAll exceptions derive from `DependencyInjectionException`.\n\n| Exception                                               | Thrown when                                                                                                     |\n|---------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|\n| `ActualTypeIsNotMatchingDecoratedTypeException`         | The `TService` type parameter does not match the class the attribute is applied to.                             |\n| `ConditionMethodHasInvalidSignatureException`           | A `[DependencyInjectionCondition]` method is not static, does not return `bool`, or has unsupported parameters. |\n| `MultipleDependencyInjectionAttributesPresentException` | A class has more than one lifetime attribute (`[Singleton]`, `[Transient]`, `[Scoped]`).                        |\n| `ServiceTypeIsNotImplementingDecoratedTypeException`    | The decorated class does not implement the `TAbstraction` type.                                                 |\n\n## Semantic Versioning\n\nThis library follows the principles of [Semantic Versioning](https://semver.org/).\n\n## Contributing\n\nContributions are welcome!\nPlease submit a pull request or create a discussion to discuss any changes you wish to make.\n\n### Code of Conduct\n\nBe excellent to each other.\n\n### Contributor License Agreement\n\nBy submitting a contribution (pull request, patch, or any other form) to this project, you agree\nto the following terms:\n\n1. **License Grant.** You grant the project maintainer (\"Maintainer\") and all recipients of the\n   software a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license to use,\n   reproduce, modify, distribute, sublicense, and otherwise exploit your contribution under the\n   terms of the GNU Lesser General Public License v3.0 (LGPL-3.0-only). You additionally grant\n   the Maintainer the right to relicense your contribution under any other open-source or\n   proprietary license at the Maintainer's sole discretion.\n\n2. **Originality.** You represent that your contribution is your original work, or that you have\n   sufficient rights to grant the licenses above. If your contribution includes third-party\n   material, you represent that its license is compatible with the LGPL-3.0-only and permits the\n   grants made herein.\n\n3. **No Conflicting Obligations.** You represent that your contribution is not subject to any\n   agreement, obligation, or encumbrance (including but not limited to employment agreements or\n   prior license grants) that would conflict with or restrict the rights granted under this\n   agreement.\n\n4. **No Compensation.** Your contribution is made voluntarily and without expectation of\n   compensation, unless separately agreed in writing.\n\n5. **Right to Remove.** The Maintainer may remove, modify, or replace your contribution at any\n   time, for any reason, without notice or obligation to you.\n\n6. **Liability.** To the maximum extent permitted by applicable law, your contribution is provided\n   \"as is\", without warranty of any kind. You shall be solely liable for any damage arising from\n   the inclusion of your contribution to the extent such damage is caused by a defect, rights\n   violation, or other issue originating in your contribution.\n\n7. **Governing Law.** This agreement is governed by the laws of the Federal Republic of Germany\n   (Bundesrepublik Deutschland), in particular the German Civil Code (BGB), without regard to\n   its conflict-of-laws provisions. For contributors outside Germany, this choice of law applies\n   to the extent permitted by the contributor's local jurisdiction.\n\nPlease add yourself to the [CONTRIBUTORS](CONTRIBUTORS.md) file when submitting your first pull\nrequest, and include the following statement in your pull request description:\n\n\u003e I have read and agree to the Contributor License Agreement in this project's README.\n\n## License\n\nThis project is licensed under the GNU Lesser General Public License v3.0.\nSee the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fx39%2Fx39.util.dependencyinjection","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fx39%2Fx39.util.dependencyinjection","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fx39%2Fx39.util.dependencyinjection/lists"}