{"id":13727810,"url":"https://github.com/lsoft/DpdtInject","last_synced_at":"2025-05-08T00:30:55.168Z","repository":{"id":40811821,"uuid":"295702441","full_name":"lsoft/DpdtInject","owner":"lsoft","description":"Highly efficient compile-time general purpose DI container based on C# source generators.","archived":false,"fork":false,"pushed_at":"2022-06-23T04:32:04.000Z","size":4319,"stargazers_count":32,"open_issues_count":17,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-08-04T02:07:42.357Z","etag":null,"topics":["csharp","csharp-sourcegenerator","dependency-injection","di-container","di-framework","inversion-of-control","ioc","performance","source-generators","sourcegenerator","visual-studio-extension","vsix-extensions"],"latest_commit_sha":null,"homepage":"https://www.nuget.org/packages/Dpdt.Injector/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lsoft.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}},"created_at":"2020-09-15T11:14:28.000Z","updated_at":"2024-04-09T18:03:16.000Z","dependencies_parsed_at":"2022-09-19T09:40:33.751Z","dependency_job_id":null,"html_url":"https://github.com/lsoft/DpdtInject","commit_stats":null,"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lsoft%2FDpdtInject","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lsoft%2FDpdtInject/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lsoft%2FDpdtInject/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lsoft%2FDpdtInject/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lsoft","download_url":"https://codeload.github.com/lsoft/DpdtInject/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224679748,"owners_count":17351857,"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":["csharp","csharp-sourcegenerator","dependency-injection","di-container","di-framework","inversion-of-control","ioc","performance","source-generators","sourcegenerator","visual-studio-extension","vsix-extensions"],"created_at":"2024-08-03T02:00:33.352Z","updated_at":"2024-11-14T19:30:23.204Z","avatar_url":"https://github.com/lsoft.png","language":"C#","readme":"# DpdtInject\n\n![Dpdt logo](logo.png)\n\n[![Compilation Status](https://github.com/lsoft/DpdtInject/actions/workflows/dpdt.yml/badge.svg)](https://github.com/lsoft/DpdtInject/actions)\n[![Nuget](https://buildstats.info/nuget/Dpdt.Injector?includePreReleases=true)](https://www.nuget.org/packages/Dpdt.Injector/)\n\n## Table Of Contents\n\n- [Purpose](#purpose)\n- [Status](#status)\n- [Design features](#design-features)\n- [Other features](#other-features)\n- [Performance](#performance)\n- [How to try](#how-to-try)\n- [Design](#design)\n  * [To be fair: design drawbacks at first place](#to-be-fair-design-drawbacks-at-first-place)\n  * [Cluster](#cluster)\n  * [Cluster life cycle](#cluster-life-cycle)\n  * [Child clusters](#child-clusters)\n  * [Syntax](#syntax)\n    + [Regular singleton/transient/custom binding](#regular-singletontransientcustom-binding)\n    + [Regular const binding](#regular-const-binding)\n    + [Conditional bindings](#conditional-bindings)\n    + [Predefined constructor arguments with additional settings](#predefined-constructor-arguments-with-additional-settings)\n    + [Proxy (and decorator at the same time) bindings](#proxy-and-decorator-at-the-same-time-bindings)\n      - [What is saver?](#what-is-saver)\n    + [Conventional bindings](#conventional-bindings)\n  * [Choosing constructor](#choosing-constructor)\n  * [Scope](#scope)\n    + [Singleton](#singleton)\n    + [Transient](#transient)\n    + [Constant](#constant)\n    + [Custom](#custom)\n  * [Conditional binding](#conditional-binding)\n  * [Fast resolutions](#fast-resolutions)\n  * [Compile-time checks](#compile-time-checks)\n    + [Did source generators are finished their job?](#did-source-generators-are-finished-their-job)\n    + [Unknown constructor argument](#unknown-constructor-argument)\n    + [Singleton takes transient or custom](#singleton-takes-transient-or-custom)\n    + [Circular dependencies](#circular-dependencies)\n    + [More than 1 unconditional child](#more-than-1-unconditional-child)\n    + [Conventional bindings](#conventional-bindings-1)\n  * [Async resolutions](#async-resolutions)\n  * [Settings](#settings)\n    + [Cross cluster resolutions](#cross-cluster-resolutions)\n      - [OnlyLocalCluster](#onlylocalcluster)\n      - [AllowedCrossCluster](#allowedcrosscluster)\n      - [MustBeCrossCluster](#mustbecrosscluster)\n    + [Wrapper producing](#wrapper-producing)\n      - [NoWrappers](#nowrappers)\n      - [ProduceWrappers](#producewrappers)\n    + [Circular checking](#circular-checking)\n      - [PerformCircularCheck](#performcircularcheck)\n      - [SuppressCircularCheck](#suppresscircularcheck)\n    + [Constructor choosing](#constructor-choosing)\n      - [AllAndOrderConstructorSetting](#allandorderconstructorsetting)\n      - [SubsetAndOrderConstructorSetting](#subsetandorderconstructorsetting)\n      - [SubsetNoOrderConstructorSetting](#subsetnoorderconstructorsetting)\n  * [Debugging your clusters and conditional clauses](#debugging-your-clusters-and-conditional-clauses)\n  * [Artifact folder](#artifact-folder)\n- [Dpdt Visual Studio Extension](#dpdt-visual-studio-extension)\n- [Known problems](#known-problems)\n- [Alternatives](#alternatives)\n- [Feedback](#feedback)\n\n\n# Purpose\n\nDpdt is a compile-time general purpose DI container based on C# Source Generators. Its goal is to remove everything possible from runtime and make resolving process as faster as we can. This is achieved by transferring huge piece of resolving logic to the compilation stage into the source generator.\n\nAs an additional concept, Dpdt adds no references to your distribution, so if you are developing a library/nuget package, you are free to use Dpdt as DI container. You will not impose your DI to your users, everything will builtin in your dlls.\n\n# Status\n\nThere is a meduim size project powered by Dpdt that runs in its production. So, I think Dpdt is in Alpha status now.\n\n# Design features\n\n0. As mentioned above, Dpdt suitable for lib/nuget developers. This **does not mean** that Dpdt is not (or less) suitable for applications.\n0. [Dpdt Visual Studio Extension](https://github.com/lsoft/DpdtInject#dpdt-visual-studio-extension) helps you to be more productive.\n0. [Additional compile-time checks](https://github.com/lsoft/DpdtInject#compile-time-checks).\n0. No performance decrease on the platforms with no compilation at runtime (because of absense runtime compilation!).\n\n# Other features\n\n0. Easy-to-read syntax `Bind\u003cIA\u003e().To\u003cA\u003e().WithTransientScope()`.\n0. Custom constructor arguments `... Configure(new ConstructorArgument(\"message\", Message))`.\n0. Generic `Get\u003cT\u003e` and non generic `Get(Type t)` resolutions.\n0. Constrained `GetFast` fast resolutions.\n0. Single object `Get` or collection `GetAll` resolutions.\n0. `Func\u003cT\u003e` resolutions.\n0. `ToIsolatedFactory` bindings with automatic factory code generation.\n0. `ToProxy` binding with automatic generate an interceptor class.\n0. Transient, singleton and constant scopes.\n0. Custom scopes.\n0. Child kernels (aka child clusters).\n0. Bindings by conventions.\n0. Binding settings which modifies compile-time code generation.\n0. At last, Dpdt produces a very, very fast resolution code (it's the last due to it's really unimportant; every modern DI fast enough for virtually all use cases).\n\nMore to come!\n\n# Performance\n\n0. Very impressive Fast resolutions.\n0. Good Generic resolution performance.\n0. Good enough, but not the best NonGeneric resolution - Microresolver is fantastically fast; what's the magic? :)\n\n``` ini\n\nBenchmarkDotNet=v0.12.0, OS=Windows 10.0.18363\nAMD Ryzen 7 4700U with Radeon Graphics, 1 CPU, 8 logical and 8 physical cores\n.NET Core SDK=5.0.100-rc.2.20479.15\n  [Host]     : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT\n  Job-OKTIPR : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT\n\nRuntime=.NET Core 3.1  Server=True  \n\n```\n\nHere is the results of a resolution a complex tree of 500 objects total:\n\n\n|                               Method |      Mean |     Error |    StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |\n|------------------------------------- |----------:|----------:|----------:|------:|------:|------:|----------:|\n|             Dpdt.GenericSingleton500 |  4.325 us | 0.0407 us | 0.0381 us |     - |     - |     - |         - |\n|          Dpdt.NonGenericSingleton500 | 13.641 us | 0.1126 us | 0.1054 us |     - |     - |     - |         - |\n|  Dpdt.FastSingleton500 (**fastest**) |  2.577 us | 0.0104 us | 0.0097 us |     - |     - |     - |         - |\n|           DryIoc.GenericSingleton500 | 16.148 us | 0.1470 us | 0.1375 us |     - |     - |     - |         - |\n|        DryIoc.NonGenericSingleton500 |  9.728 us | 0.1689 us | 0.1580 us |     - |     - |     - |         - |\n|    Microresolver.GenericSingleton500 |  7.276 us | 0.0573 us | 0.0536 us |     - |     - |     - |         - |\n| Microresolver.NonGenericSingleton500 |  3.784 us | 0.0158 us | 0.0148 us |     - |     - |     - |         - |\n\n\n|                               Method |     Mean |    Error |   StdDev |  Gen 0 | Gen 1 | Gen 2 | Allocated |\n|------------------------------------- |---------:|---------:|---------:|-------:|------:|------:|----------:|\n|             Dpdt.GenericTransient500 | 40.32 us | 0.330 us | 0.309 us | 4.1504 |     - |     - |  77.02 KB |\n|          Dpdt.NonGenericTransient500 | 63.08 us | 0.489 us | 0.433 us | 4.1504 |     - |     - |  77.02 KB |\n|  Dpdt.FastTransient500 (**fastest**) | 37.93 us | 0.253 us | 0.224 us | 3.8452 |     - |     - |   71.2 KB |\n|           DryIoc.GenericTransient500 | 66.87 us | 1.288 us | 1.581 us | 4.2725 |     - |     - |  77.02 KB |\n|        DryIoc.NonGenericTransient500 | 62.60 us | 0.480 us | 0.449 us | 4.1504 |     - |     - |  77.02 KB |\n|    Microresolver.GenericTransient500 | 64.77 us | 0.294 us | 0.261 us | 4.2725 |     - |     - |  77.02 KB |\n| Microresolver.NonGenericTransient500 | 68.52 us | 2.869 us | 8.415 us | 4.1504 |     - |     - |  77.02 KB |\n\t\n\n\nAlso I recommend disable tiered compilation for composition root assembly if you want to obtain full performance at the start.\n\n\n# How to try\n\n## Without Dpdt Visual Studio Extension\n\n- Create new Console Application in Visual Studio or `dotnet` console command. Keep in mind you need to set `net5` or `net6` target framework. \n- Install latest `Dpdt.Injector` [nuget package](https://www.nuget.org/packages/Dpdt.Injector/). Keep in mind that nuget may be in the prerelease state.\n- (optional) You can disable tiered compilation for composition root assembly and set `EmitCompilerGeneratedFiles` to `true`.\n- At this point, you have something like the following in your csproj file:\n\n```xml\n\u003cProject Sdk=\"Microsoft.NET.Sdk\"\u003e\n\n  \u003cPropertyGroup\u003e\n    \u003cOutputType\u003eExe\u003c/OutputType\u003e\n    \u003cTargetFramework\u003enet5\u003c/TargetFramework\u003e\n\n    \u003c!-- disable tiered compilation for composition root assembly --\u003e\n    \u003cTieredCompilation\u003efalse\u003c/TieredCompilation\u003e\n    \u003cTieredCompilationQuickJit\u003efalse\u003c/TieredCompilationQuickJit\u003e\n    \u003cTieredCompilationQuickJitForLoops\u003efalse\u003c/TieredCompilationQuickJitForLoops\u003e\n\n    \u003cEmitCompilerGeneratedFiles\u003etrue\u003c/EmitCompilerGeneratedFiles\u003e\n  \u003c/PropertyGroup\u003e\n\n  \u003cItemGroup\u003e\n    \u003cPackageReference Include=\"Dpdt.Injector\" Version=\"0.8.3-alpha\" /\u003e\n  \u003c/ItemGroup\u003e\n\n\u003c/Project\u003e\n```\n\n- Next, create a class which will be resolved from a Dpdt container, for example: `public class MyPayload { }`\n- You will need a Dpdt cluster class:\n\n```csharp\n    public partial class MyCluster : DpdtInject.Injector.Src.DefaultCluster\n    {\n        [DpdtInject.Injector.Src.DpdtBindingMethod]\n        public void Bind()\n        {\n            Bind\u003cMyPayload\u003e()\n                .To\u003cMyPayload\u003e()\n                .WithSingletonScope()\n                ;\n        }\n    }\n```\n\n- Now, it's time to create a cluster and take our payload from it; put the following at `Program.Main`:\n\n```csharp\n    /*await/* var cluster = new MyCluster(null); //await is needed to dispose singletons with IAsyncDisposable, but without IDisposable interfaces\n    var payload = cluster.Get\u003cMyPayload\u003e();\n    Console.WriteLine(payload.GetType().Name);\n```\n\n- Finally, run your program.\n\n## With Dpdt Visual Studio Extension\n\n- Install Dpdt Visual Studio Extension for [Visual Studio 2019](https://marketplace.visualstudio.com/items?itemName=lsoft.DpdtVisualStudioExtension) or for [Visual Studio 2022](https://marketplace.visualstudio.com/items?itemName=lsoft.DpdtVisualStudioExtension2022).\n- Restart Visual Studio.\n- Create new Console Application in Visual Studio. Keep in mind you need to set `net5` or `net6` target framework. \n- (optional) You can disable tiered compilation for composition root assembly and set `EmitCompilerGeneratedFiles` to `true`.\n- Install the latest Dpdt Nuget Package via [context menu](extension4.png)\n- Create Dpdt cluster and binding method via [context menu](extension4.png) and the [tool window](extension5.png)\n- Next, create a class which will be resolved from a Dpdt container, for example: `public class MyPayload { }`\n- Add the binding clause to the `Bind` method of the produced cluster:\n\n```csharp\nBind\u003cMyPayload\u003e()\n    .To\u003cMyPayload\u003e()\n    .WithSingletonScope()\n    ;\n```\n\n- Now, it's time to create a cluster and take our payload from it; put the following at `Program.Main`:\n\n```csharp\n    /*await*/ using var cluster = new MyCluster(null); //await is needed to dispose singletons with IAsyncDisposable, but without IDisposable interfaces\n    var payload = cluster.Get\u003cMyPayload\u003e();\n    Console.WriteLine(payload.GetType().Name);\n```\n\n- Finally, run your program.\n\n# Design\n\n## To be fair: design drawbacks at first place\n\n0. Because of design, it's impossible to `Unbind` and `Rebind`.\n0. Because of source generators, it's impossible to direclty debug your bind code, including its `When` predicates.\n0. Because of massive rewriting the body of the cluster, it's impossible to use a local variables (local methods and other local stuff) in `ConstructorArgument` and `When` predicates. To make bind works use instance based fields, properties and methods instead. To make bind debuggable, use fields, properties and methods of the other, helper class.\n0. No deferred bindings by design with exception of cluster hierarchy.\n0. Slower source-to-IL compilation, slower JIT compilation.\n\n## Cluster\n\nDpdt bingings organized in the groups named `clusters`. Cluster is a class that derived from Dpdt's `DpdtInject.Injector.Src.DefaultCluster`. This class should be `partial`. Each cluster may have any numbers of binding methods even in different compilation units. These methods should be marked with attribute `[DpdtBindingMethod]`. No argument allowed for that methods, and in fact they are not executed at all. You can use this to split your bindings into different groups (something like Ninject's modules).\n\n\n## Cluster life cycle\n\nThe life cycle of the cluster begins by creating it with `new`. The cluster can take other cluster as its parent, so each unknown dependency will be resolved from the parent (if parent exists, otherwise exception would be thrown).\n\nThe end of the life cycle of a cluster occurs after the call to its `Dispose` method. At this point, all of its disposable singleton bindings are also being disposed. It is prohibited to dispose of the cluster and use it for resolving in parallel . It is forbidden to resolve after a `Dispose`.\n\nIf you have at least one singleton with `IAsyncDisposable` interface, but without `IDisposable` interface, you need to invoke `DisposeAsync` instead of `Dispose` at the cluster object. The rule is simple: `Dispose` cluster -\u003e `Dispose` its singletons, `AsyncDispose` cluster -\u003e `Dispose` + `DisposeAsync` its singletons. So, I recommend always use `DisposeAsync` for a cluster objects. **Disposing order is undefined.**\n\n\n## Child clusters\n\n```csharp\n    public partial class RootCluster : DefaultCluster\n    {\n        public RootCluster(your arguments) : this((ICluster)null!) { ... }\n    }\n\n    public partial class ChildCluster : DefaultCluster\n    {\n        public ChildCluster(ICluster cluster, your arguments) : this(cluster) { ... }\n    }\n\n...\n\n    /*await*/ using var rootCluster = new RootCluster(\n        your arguments\n        );\n    /*await*/ using var childCluster = new ChildCluster(\n        rootCluster,\n        your arguments\n        );\n```\n\nClusters are organized into a tree. This tree cannot have a circular dependency, since it is based on constructor argument. Dependencies, consumed by the binding in the child cluster, are resolved from the home cluster if exists, if not - from **parent cluster**.\n\nIf some binging does not exist in local cluster, Dpdt will request it from parent cluster at runtime. This behavior can be modified by settings `OnlyLocalCluster`/`AllowedCrossCluster`/`MustBeCrossCluster`.\n\nChild clusters must be disposed BEFORE its parent.\n\n\n## Syntax\n\nDpdt syntax was partially inspired by Ninject. A lot of examples of allowed syntaxes are available in the test project. Please refer that code.\n\n### Regular singleton/transient/custom binding\n\n```csharp\nBind\u003cIB1, IB2\u003e()\n    .To\u003cBClass\u003e()\n    .WithSingletonScope() //WithTransientScope, WithCustomScope\n    ;\n```\n\n### Regular const binding\n\nOnly readonly fields, static readonly fields and in-place compile-time constants are allowed to be a target constant:\n\n\n```csharp\nprivate /*static*/ readonly string _roString = \"readonly string\";\n\n...\n\nBind\u003cstring\u003e()\n    .WithConstScope(_roString)\n    ;\n\n\nBind\u003cstring\u003e()\n    .WithConstScope(\"some inplace string\")\n    ;\n\n```\n\n### Conditional bindings\n\n```csharp\nBind\u003cIA\u003e()\n    .WithConstScope(ConstantA)\n    .When(rt =\u003e rt.ParentTarget?.TargetType == typeof(B2))\n    ;\n```\n\n### Predefined constructor arguments with additional settings\n\n```csharp\nBind\u003cIB\u003e()\n     .To\u003cB\u003e()\n     .WithSingletonScope()\n     .Setup\u003cAllowedCrossCluster\u003e()\n     .Setup\u003cSubsetNoOrderConstructorSetting\u003cint, long\u003e\u003e() //imagine that argument1 is int, and argument2 is long; note: you are NOT forced to use constructor setting if you are using ConstructorArgument\n     .Configure(new ConstructorArgument(\"argument1\", field_or_property_or_expression1))\n     .Configure(new ConstructorArgument(\"argument2\", field_or_property_or_expression2))\n     ;\n```\n\n### Proxy (and decorator at the same time) bindings\n\n```csharp\n\n//your custom telemetry event saver\n//saver is invoked from multiple threads even in parallel\n//so saver must be thread-safe\nBind\u003cSessionSaver\u003e()\n    .To\u003cSessionSaver\u003e()\n    .WithSingletonScope()\n    ;\n\n//example of the payload, that will be injected into the proxy:\nBind\u003cICalculator\u003e()\n    .To\u003cCalculator\u003e()\n    .WithSingletonScope() //may be singleton or transient\n    .When(rt =\u003e rt.WhenInjectedExactlyInto\u003cProxyCalculator\u003e())\n    ;\n\n\n//proxy binding example\n//proxy invokes its saver for every invocation of the proxied methods, EVEN in parallel!\nBind\u003cICalculator\u003e()\n    .ToProxy\u003cProxyCalculator\u003e()\n    .WithProxySettings\u003cTelemetryAttribute, SessionSaver\u003e() //additional details about these classes are available at the tests project\n    .WithSingletonScope() //proxy should be in the same scope as its payload\n    .Setup\u003cSuppressCircularCheck\u003e() //this suppress unused warning\n    .When(rt =\u003e rt.WhenInjectedExactlyNotInto\u003cProxyCalculator\u003e()) //this suppress stack overflow during resolution\n    ;\n\n//\"proto\" class of the proxy:\npublic partial class ProxyCalculator : ICalculator { }\n\n```\n\nDpdt proxy can decorate a methods, properties (get/set), events (add/remove) and indexers.\n\n#### What is saver?\n\nSaver is an interceptor class that is used by the Proxy-decorator to convey some info about the current invocation. Saver MUST be thread-safe!\n\nSaver has 2 methods:\n\n0. StartSessionSafely\n0. FixSessionSafely\n\nSuffix `Safely` means that this method should not raise any exception; please wrap their bodies into `try`-`catch`.\n\n`StartSessionSafely` is invoked at the beginning of decorated (proxied) method. Its arguments:\n\n0. `fullClassName` contains a full class name (e.g. `Dpdt.Injector.DefaultCluster`)\n0. `memberName` contains a method\\property\\event name, or  `long this[]` for an indexer, where `long` is a indexer return type\n0. `arguments` contains an array of member arguments; imagine a method `void DoSomething(int a, long b)`, in this case `arguments` will contain: name of argument a (e.g. string \"a\"), value of argument a (e.g. some int), name of argument b (e.g. string \"b\"), value of argument b (e.g. some long); if no arguments exists `arguments` may be `null`\n\nPlease make note: parameters with `out` modifier will not appear in `arguments`.\n\n`StartSessionSafely` returns a newly created `Guid`.\n\n`FixSessionSafely` is invoked at the end of proxied-method. Its arguments:\n\n0. `sessionGuid` contains a Guid that earlier has been returned from `StartSessionSafely`\n0. `takenInSeconds` contains a time (a fraction of seconds) that has been spent in decorated member\n0. `exception` contains an exception in the case if decorated member raises an exception, otherwise `null`\n\nIn the wild, session class usually uses `ConcurrentDictionary` to store sessions in `StartSessionSafely`. In `FixSessionSafely` you can do `TryRemove`, append the timings and exception, and send this completed session to the logger or whatever you need/want.\n\n\n### Conventional bindings\n\nConventional bindings are \"machine-gun\" binding producing machine, that scans your assemblies and produces a lot of binding statements. If you are know Ninject.Conventions you know what I wanted to implement. Conventional bindings works with Roslyn symbols and are produced at compile-time.\n\n```csharp\nScanInAssembliesWith\u003cA0\u003e() //scan assembly(ies) that contains a specified type(s)\n    .SelectAllWith\u003cIA\u003e() //select all classes that implements a specific class/interface\n    .ExcludeAllWith\u003cIExclude\u003e() //but not to include all classes that implements this class/interface\n    .From\u003cIA, IB\u003e() //binds from these interfaces/classes (they may be different with the classes/interfaces in SelectAllWith); others options are exists too\n    .ToItself() //to the processed class\n    .WithSingletonScope() //and now we continue with regular binding syntax, including With*Scope/Setup/Configure/When and etc.\n    ;\n```\n\nExample how to bind from open generic:\n\n```csharp\n\n//the following statement will bind A0 and A2; A1 will be excluded due to IExclude\u003c\u003e\nScanInAssembliesWith\u003cA0\u003e()\n    .SelectAllWithOpenGeneric\u003cIA\u003cobject\u003e\u003e() //generic argument 'object' means NOTHING! it will be removed by Dpdt, so IA\u003cobject\u003e will be transformed into IA\u003c\u003e (open generic)\n    .ExcludeAllWithOpenGeneric\u003cIExclude\u003cobject\u003e\u003e() //again, 'object' means nothing, as above\n    .FromAllInterfaces()\n    .ToItself()\n    .WithSingletonScope()\n    ;\n\n\npublic interface IA { }\npublic interface IA\u003cT1\u003e : IA { }\npublic interface IExclude\u003cT\u003e { }\npublic class A0 : IA\u003cobject\u003e { }\npublic class A1 : IA\u003cstring\u003e, IExclude\u003culong\u003e { }\npublic class A2 : IA\u003cStringBuilder\u003e { }\n```\n\nExample how to exclude an item from a conventional binding. Imagine you have the following:\n\n```csharp\n//this statement will bind A0 A1 A2 classes\nScanInAssembliesWith\u003cA0\u003e()\n    .SelectAllWith\u003cIA\u003e()\n    .From\u003cIA\u003e()\n    .ToItself()\n    .WithSingletonScope()\n    ;\n```\n\nbut you want to bind `A1` in a slightly different way. You can easily do that:\n\n```csharp\n//this statement will bind A0 A2 classes\nScanInAssembliesWith\u003cA0\u003e()\n    .SelectAllWith\u003cIA\u003e()\n    .ExcludeAllWith\u003cA1\u003e() //only append this\n    .From\u003cIA\u003e()\n    .ToItself()\n    .WithSingletonScope()\n    ;\n\n//and add a new binding statement\nBind\u003cIA\u003e()\n    .To\u003cA1\u003e()\n    .WithTransientScope() //here is the difference: transient instead of singleton\n    ;\n```\n\nDpdt does not support conventional bindings to proxy or factory, because of design.\n\n## Choosing constructor\n\nConstructor is chosen at the compilation stage based on 3 principles:\n\n0. If constructor filtering setting is set, it will be applied against existing constructors.\n0. If any `ConstructorArgument` filter are set, they will be applied against constructors filtered by previous step. If no `ConstructorArgument` has defined, all filtered constructors will be taken.\n0. After all filtering, the constructor with the minimum number of parameters is selected to make a binding.\n\n## Scope\n\nBind clause with no defined scope raises a question: an author forgot set a scope or wanted a default scope? We make a decision not to have a default scope and force a user to define a scope.\n\n### Singleton\n\nThe only one instance of defined type is created. If instance is `IDisposable` then `Dispose` method will be invoked at the moment the cluster is disposing. This operation is thread safety, double checking locking algorithm is on.\n\n### Transient\n\nEach resolution call results with new instance. `Dispose` for targets will not be invoked.\n\n### Constant\n\nConstant scope is a scope when the cluster receive an outside-created object. Its `Dispose` will not be invoked, because the cluster is not a parent of the constant object.\n\n### Custom\n\n```csharp\n    Bind\u003cIA\u003e()\n        .To\u003cA\u003e()\n        .WithCustomScope()\n        ;\n\n...\n\n    /*await/* using(var scope1 = cluster.CreateCustomScope())\n    {\n        var a1 = cluster.Get\u003cIA\u003e(scope1);\n\n        /*await/* using(var scope2 = cluster.CreateCustomScope())\n        {\n            var a2 = cluster.Get\u003cIA\u003e(scope2);\n        } //here we dispose a2 if target for IA is IDisposable /*or IAsyncDisposable*/\n    } //here we dispose a1 if target for IA is IDisposable /*or IAsyncDisposable*/\n```\n\n`IDisposable` custom-binded objects will be disposed at the moment of the scope object dispose. DO NOT forget to invoke `Dispose` on scope object! Otherwise, Custom-scoped disposable objects will not be disposed too.\n\nIf you have at least one custom scoped binding with `IAsyncDisposable` interface, but without `IDisposable` interface, you need to invoke `DisposeAsync` instead of `Dispose` at the scope object. The rule is simple: `Dispose` scope object -\u003e `Dispose` its bindings, `AsyncDispose` scope object -\u003e `Dispose` + `DisposeAsync` its bindings. So, I recommend always use `DisposeAsync` for a scope objects. **Disposing order is undefined.**\n\n\n\n Keep in mind, custom-scoped bindings are resolved much slower than singleton/transient/constant bindings.\n\n## Conditional binding\n\nEach bind clause may have an additional filter e.g.\n\n```csharp\n    Bind\u003cIA\u003e()\n        .To\u003cA\u003e()\n        .WithSingletonScope()\n        .When(IResolutionTarget rt =\u003e\n        {\n                condition to resolve\n        })\n    ;\n```\n\nPlease refer unit tests to see the examples. Please note, than any filter makes a resolution process slower (a much slower! 10x slower in compare of unconditional binding!), so use this feature responsibly. Resolution slowdown with conditional bindings has an effect even on those bindings that do not have conditions, but they directly or indirectly takes conditional binding as its dependency. Therefore, it is advisable to place conditions as close to the resolution root as possible.\n\nAlso, these predicates cannot be debugged because they are rewrited by Dpdt. See below how to overcome it.\n\n## Fast resolutions\n\nDpdt contains a special resolution type named 'fast'. Its syntax is `cluster.GetFast(default(IMyInterface));`. In general this syntax is faster that generic resolutions, but it has one additional constraint: you need to resolve directly from cluster type, it is impossible to cast cluster to the one of its interface (like `ICluster` or `IResolution`) and do fast resolutions.\n\n## Compile-time checks\n\nEach safety checks are processed in the scope of concrete cluster. Dpdt cannot check for cross-cluster issues because clusters tree is built at runtime. But cross-cluster checks are performed in the cluster constructor at runtime.\n\n### Did source generators are finished their job?\n\nDpdt adds a warning to compilation log with the information about how many clusters being processed and time taken. It's an useful bit of information for debugging purposes.\n\n### Unknown constructor argument\n\nDpdt will break ongoing compilation if binding has useless `ConstructorArgument` clause (no constructor with this parameter exists).\n\n### Singleton takes transient or custom (captive dependency)\n\nDpdt can detect cases of singleton binding takes a transient/custom binding as its dependency, and make signals to the programmer. It's not always a bug, but warning might be useful.\n\n### Circular dependencies\n\nDpdt is available to determine circular dependencies in your dependency tree. In that cases it raise a compilation error. One additional point: if that circle contains a conditional binding, Dpdt can't determine if circular dependency will exists at runtime, so Dpdt raises a compile-time warning instead of error. This behaviour can be changed by appropriate setting.\n\n### More than 1 unconditional child\n\nIf for some binding more than 1 unconditional child exists it renders parent unresolvable, so Dpdt will break the compilation is that case.\n\n### Conventional bindings\n\nDpdt will write some info in Build log for every conventional binding produced. For regular binding - will not. It is useful for debugging conventional bindings results.\n\n\n## Async resolutions\n\nDpdt is a constructor-based injector. Async resolutions are not supported because we have no an async constructors. Consider using a factory class with an async method `async Task\u003cSomething\u003e CreateSomethingAsync(...)`.\n\n\n## Settings\n\nSettings are things that modify compile-time cluster code generation. THEY ARE NOT WORKING AT RUNTIME.\n\n### Cross cluster resolutions\n\nThese settings relates with checking for child binding resolutions; they are useful for an additional safety. They are applied for each of child resolutions.\n\n#### OnlyLocalCluster\n\nEach dependency **must** exists in local cluster. If not - ongoing compilation will break. Note: binding conditions is out of scope, only existing matters. You can define a local binding with `When(rt =\u003e false)`, and this check will mute. So, this setting is not something that can protect you at 100%. This is a default behaiour.\n\n#### AllowedCrossCluster\n\nAny dependency may be in home cluster or parent cluster. If local dependecy found at runtime it is used, otherwise request to the parent cluster is performed. (this is default behaviour for old version of Dpdt)\n\n#### MustBeCrossCluster\n\nNO local dependency allowed, any dependency MUST be in the parent cluster. If local dependency found, ongoing compilation will break. Note: binding conditions is out of scope, only existing matters. You can define a local binding with `When(rt =\u003e false)`, and this check will fire. So, this setting is not something that can protect you at 100%.\n\n\n### Wrapper producing\n\nThese settings relate with a producing of wrapped-resolutions, like `Func\u003c\u003e`; they are  useful for minimizing the cluster size.\n\n#### NoWrappers\n\nNo bindings for wrappers will be produced. It is a default value.\n\n#### ProduceWrappers\n\n**Every** type of wrappers will be produced for this binding.\n\n\n### Circular checking\n\nThese settings relates with a circular checking; they are useful for removing unused noise from build log (for example in case of decorator, look at `ProxySimple0_Fixture` unit test).\n\n#### PerformCircularCheck\n\nDo circular checking. It is a default value.\n\n#### SuppressCircularCheck\n\nDo not circular checking. Use this for decorators bindings.\n\n\n### Constructor choosing\n\nThese settings relates with a constructor choosing algorithm performed by Dpdt. These settings sets a constructor argument **types** filter.\n\n#### AllAndOrderConstructorSetting\n\nDpdt will take the only constructor that have a parameters with the types selected in this setting and in the same order. For example `.Setup\u003cAllAndOrderConstructorSetting\u003cint, long\u003e\u003e()` means that only constructor `MyClass(int a, long b)` will choose (argument names does not matters).\n\n#### SubsetAndOrderConstructorSetting\n\nDpdt will take the only constructors that have a parameters with the types selected in this setting and in the same order, and additional arguments may exists before or after the selected. For example `.Setup\u003cSubsetAndOrderConstructorSetting\u003cint, long\u003e\u003e()` will choose the following constructors `MyClass(..., int b, long c)` `MyClass(int a, long b)` `MyClass(int a, long b, ...)`.\n\n#### SubsetNoOrderConstructorSetting\n\nDpdt will take the only constructors that have a parameters with the types selected in this setting and their order does not matters, any additional arguments may exists. For example `.Setup\u003cSubsetNoOrderConstructorSetting\u003cint, long\u003e\u003e()` will choose the following constructors `MyClass(long a, int b)` `MyClass(int a, long b)` `MyClass(..., int a, ... long b, ...)` `MyClass(..., long a, ..., int b, ...)`.\n\nPlease make note: `in` `readonly` and `ref` modifiers of the constructor arguments will not taken into account.\n\n\n## Debugging your clusters and conditional clauses\n\nBecause of source generators are generating new code based on your code, it's impossible to direclty debug your cluster code, including its `When` predicates (because this code is not actually executed at runtime). It's a disadvantage of Dpdt design. For conditional clauses, you need to call another class to obtain an ability to catch a breakpoint:\n\n```csharp\n    public partial class MyCluster : DefaultCluster\n    {\n        [DpdtBindingMethod]\n        public void BindMethod()\n        {\n            Bind\u003cIA, IA2\u003e()\n                .To\u003cA\u003e()\n                .WithSingletonScope()\n                .When(rt =\u003e\n                {\n                    //here debugger is NOT working\n\n                    return Debugger.Debug(rt);\n                })\n                ;\n        }\n    }\n\n    public static class Debugger\n    {\n        public static bool Debug(IResolutionTarget rt)\n        {\n            //here debugger is working\n            return true;\n        }\n    }\n```\n\n## Artifact folder\n\nAs a regular source generator, Dpdt is able to store pregenerated C# code at the disk. The only thing you need is correctly setup your csproj. For example:\n\n```xml\n    \u003cEmitCompilerGeneratedFiles\u003etrue\u003c/EmitCompilerGeneratedFiles\u003e\n    \u003c!-- next line allows you to define a custom directory to store Dpdt's artifacts, for example: --\u003e\n    \u003c!-- \u003cCompilerGeneratedFilesOutputPath\u003e$(ProjectDir)Dpdt.Pregenerated\u003c/CompilerGeneratedFilesOutputPath\u003e --\u003e\n```\n\nIf your clusters are huge, you may face with slowdowns in your work in VS because VS runs Dpdt in the background. To overcome this please put the following into your csproj:\n\n```xml\n  \u003cPropertyGroup\u003e\n    \u003cDpdt_Generator_Beautify\u003efalse\u003c/Dpdt_Generator_Beautify\u003e\n  \u003c/PropertyGroup\u003e\n\n  \u003cItemGroup\u003e\n    \u003cCompilerVisibleProperty Include=\"Dpdt_Generator_Beautify\" /\u003e\n  \u003c/ItemGroup\u003e\n\n```\nThis is to turn off code beautification so Dpdt will produce cluster code a bit faster.\n\n# Dpdt Visual Studio Extension\n\nTo make a dealing with Dpdt easier, a Visual Studio Extension has been developed. Make note: it only supports [Visual Studio 2019 16.8 (and later)](https://marketplace.visualstudio.com/items?itemName=lsoft.DpdtVisualStudioExtension) and [Visual Studio 2022](https://marketplace.visualstudio.com/items?itemName=lsoft.DpdtVisualStudioExtension2022).\n\nPlease make note: Dpdt Visual Studio extension should be the SAME version as your Dpdt.Injector package version. If it is not possible, please use the latest version of the both. I carefully try to publish compatible versions of the new nuget and new vsixes at the same time. Sometimes, a new version of vsix can be published without nuget, and the opposite. It means that the new version of vsix (or nuget) will be compatible with last existing version of the nuget (or vsix).\n\nIf you click on a project in Solution Explorer and there is no Dpdt nuget installed, you can install its latest version easily:\n\n![Dpdt Extension Image 4](extension4.png)\n\nAlso, you can create a new cluster class or add a new binding method to the existing cluster:\n\n![Dpdt Extension Image 5](extension5.png)\n\nThere is a tool window to look, search and go to any binding in your solution:\n\n![Dpdt Extension Image 6](extension6.png)\n\nYou can enter a text to search everywhere, or use the following predicates to set a search scope:\n\n- `from:` to search only in bind from fields\n- `to:` to search only in bind to field\n- `ca:` to search only in constructor arguments field\n- `other` to search only in the scope field\n- `location` to search only in binding location field\n\nKeep in mind: the searching is case sensitive (like C# too). Examples: `MyClass`, `ca:MyConstructorArgument`, `location:MyFile.cs` etc.\n\nBut the main function is to generate a binding clauses through the custom codelens. The following images makes the picture brighter:\n\n![Dpdt Extension Image 0](extension0.png)\n\n![Dpdt Extension Image 1](extension1.png)\n\n![Dpdt Extension Image 2](extension2.png)\n\nSettings window:\n\n![Dpdt Extension Image 3](extension3.png)\n\n`EnableWhitespaceNormalization` sets whitespace normalization when you create a new binding through Dpdt Visual Studio Extension.\n\nA lot of thanks to bert2 and his amazing example `https://github.com/bert2/microscope`, without his `microscope` no Dpdt extension will appear because of lack of tutorials in the scope of VS extension development.\n\n# Known problems\n\n* Dpdt extension do not support few types (in different assemblies) with the same full name. I will investigate it further.\n\nIf any problem occurs with this extension or the generator itself, please let me know. I will need to see the following log files `C:\\Users\\\u003cuser\u003e\\AppData\\Local\\Temp\\dpdt_*.log`.\n\n# Alternatives\n\nYou may be interesting in the following alternatives:\n\n- [Strong Inject](https://github.com/YairHalberstadt/stronginject)\n\n\n# Feedback\n\nAny ideas for new features of Dpdt and Dpdt Visual Studio Extension are welcome.\n\nFeel free to send a feedback by creating an issues here. Cheers!\n","funding_links":[],"categories":["Demo, PoC and excercise projects","Source Generators"],"sub_categories":["Other"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flsoft%2FDpdtInject","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flsoft%2FDpdtInject","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flsoft%2FDpdtInject/lists"}