Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/DevTeam/Pure.DI
Pure DI for .NET without frameworks!
https://github.com/DevTeam/Pure.DI
csharp-sourcegenerator dependency-injection di dotnet injection-container injection-framework inversion-of-control ioc pure solid
Last synced: 2 days ago
JSON representation
Pure DI for .NET without frameworks!
- Host: GitHub
- URL: https://github.com/DevTeam/Pure.DI
- Owner: DevTeam
- License: mit
- Created: 2021-03-28T05:36:20.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2024-04-13T18:03:36.000Z (7 months ago)
- Last Synced: 2024-04-13T19:53:52.728Z (7 months ago)
- Topics: csharp-sourcegenerator, dependency-injection, di, dotnet, injection-container, injection-framework, inversion-of-control, ioc, pure, solid
- Language: C#
- Homepage:
- Size: 59.8 MB
- Stars: 372
- Watchers: 5
- Forks: 20
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- RSCG_Examples - Pure.DI
- csharp-source-generators - Pure.DI - ![stars](https://img.shields.io/github/stars/DevTeam/Pure.DI?style=flat-square&cacheSeconds=604800) ![last commit](https://img.shields.io/github/last-commit/DevTeam/Pure.DI?style=flat-square&cacheSeconds=86400) - dependency injection for .NET without any IoC/DI containers, frameworks, dependencies, and thus without any performance impact and side-effects. (Source Generators / Dependency Injection (IoC Container))
README
# Pure DI for .NET
[![NuGet](https://img.shields.io/nuget/v/Pure.DI)](https://www.nuget.org/packages/Pure.DI)
[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Build](https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:OpenSourceProjects_DevTeam_PureDi_BuildAndTestBuildType)/statusIcon)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=OpenSourceProjects_DevTeam_PureDi_BuildAndTestBuildType&guest=1)
[![Performance Build](https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:OpenSourceProjects_DevTeam_PureDi_PerformanceTests)/statusIcon)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=OpenSourceProjects_DevTeam_PureDi_PerformanceTests&guest=1)
![GitHub Build](https://github.com/DevTeam/Pure.DI/actions/workflows/main.yml/badge.svg)![](readme/di.gif)
_Supports .NET starting with .NET Framework 2.0, released 2005-10-27, and all newer versions._
## Usage requirements
- .NET SDK [6.0.4](https://dotnet.microsoft.com/download/dotnet/6.0) or later is installed
- Using [C# 8](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-80) or later version## Key features
Pure.DI is not a framework or library, but a source code generator for creating object graphs. To make them accurate, the developer uses a set of intuitive hints from the Pure.DI API. During the compilation phase, Pure.DI determines the optimal graph structure, checks its correctness, and generates partial class code to create object graphs in the Pure DI paradigm using only basic language constructs. The resulting generated code is robust, works everywhere, throws no exceptions, does not depend on .NET library calls or .NET reflections, is efficient in terms of performance and memory consumption, and is subject to all optimizations. This code can be easily integrated into an application because it does not use unnecessary delegates, additional calls to any methods, type conversions, boxing/unboxing, etc.
- [X] DI without any IoC/DI containers, frameworks, dependencies and hence no performance impact or side effects.
>_Pure.DI_ is actually a [.NET code generator](https://docs.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview). It uses basic language constructs to create simple code as well as if you were doing it yourself: de facto it's just a bunch of nested constructor calls. This code can be viewed, analyzed at any time, and debugged.
- [X] A predictable and verified dependency graph is built and validated on the fly while writing code.
>All logic for analyzing the graph of objects, constructors and methods takes place at compile time. _Pure.DI_ notifies the developer at compile time of missing or cyclic dependencies, cases when some dependencies are not suitable for injection, etc. The developer has no chance to get a program that will crash at runtime because of some exception related to incorrect object graph construction. All this magic happens at the same time as the code is written, so you have instant feedback between the fact that you have made changes to your code and the fact that your code is already tested and ready to use.
- [X] Does not add any dependencies to other assemblies.
>When using pure DI, no dependencies are added to assemblies because only basic language constructs and nothing more are used.
- [X] Highest performance, including compiler and JIT optimization and minimal memory consumption.
>All generated code runs as fast as your own, in pure DI style, including compile-time and run-time optimization. As mentioned above, graph analysis is done at compile time, and at runtime there are only a bunch of nested constructors, and that's it. Memory is spent only on the object graph being created.
- [X] It works everywhere.
>Since the pure DI approach does not use any dependencies or [.NET reflection](https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/reflection) at runtime, it does not prevent the code from running as expected on any platform: Full .NET Framework 2.0+, .NET Core, .NET, UWP/XBOX, .NET IoT, Xamarin, Native AOT, etc.
- [X] Ease of Use.
>The _Pure.DI_ API is very similar to the API of most IoC/DI libraries. And this was a conscious decision: the main reason is that programmers don't need to learn a new API.
- [X] Superfine customization of generic types.
>In _Pure.DI_ it is proposed to use special marker types instead of using open generic types. This allows you to build the object graph more accurately and take full advantage of generic types.
- [X] Supports the major .NET BCL types out of the box.
>_Pure.DI_ already [supports](#base-class-library) many of [BCL types](https://docs.microsoft.com/en-us/dotnet/standard/framework-libraries#base-class-libraries) like `Array`, `IEnumerable`, `IList`, `IReadOnlyCollection`, `IReadOnlyList`, `ISet`, `IProducerConsumerCollection`, `ConcurrentBag`, `Func`, `ThreadLocal`, `ValueTask`, `Task`, `MemoryPool`, `ArrayPool`, `ReadOnlyMemory`, `Memory`, `ReadOnlySpan`, `Span`, `IComparer`, `IEqualityComparer` and etc. without any extra effort.
- [X] Good for building libraries or frameworks where resource consumption is particularly critical.
>Its high performance, zero memory consumption/preparation overhead, and lack of dependencies make it ideal for building libraries and frameworks.
![](di.gif)## Schrödinger's cat will demonstrate how it all works [![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](samples/ShroedingersCat)
### The reality is
![Cat](readme/cat.png?raw=true)
### Let's create an abstraction
```c#
interface IBox
{
T Content { get; }
}interface ICat
{
State State { get; }
}enum State { Alive, Dead }
```### Here's our implementation
```c#
record CardboardBox(T Content): IBox;class ShroedingersCat(Lazy superposition): ICat
{
// The decoherence of the superposition
// at the time of observation via an irreversible process
public State State => superposition.Value;
}
```> [!IMPORTANT]
> Our abstraction and implementation knows nothing about the magic of DI or any frameworks.### Let's glue it all together
Add the _Pure.DI_ package to your project:
[![NuGet](https://img.shields.io/nuget/v/Pure.DI)](https://www.nuget.org/packages/Pure.DI)
Let's bind the abstractions to their implementations and set up the creation of the object graph:
```c#
DI.Setup(nameof(Composition))
// Models a random subatomic event that may or may not occur
.Bind().As(Singleton).To()
// Quantum superposition of two states: Alive or Dead
.Bind().To((Random random) => (State)random.Next(2))
.Bind().To()
// Cardboard box with any contents
.Bind().To>()
// Composition Root
.Root("Root");
```> [!NOTE]
> In fact, the `Bind().As(Singleton).To()` binding is unnecessary since _Pure.DI_ supports many .NET BCL types out of the box, including [Random](https://github.com/DevTeam/Pure.DI/blob/27a1ccd604b2fdd55f6bfec01c24c86428ddfdcb/src/Pure.DI.Core/Features/Default.g.cs#L289). It was added just for the example of using the _Singleton_ lifetime.The above code specifies the generation of a partial class named *__Composition__*, this name is defined in the `DI.Setup(nameof(Composition))` call. This class contains a *__Root__* property that returns a graph of objects with an object of type *__Program__* as the root. The type and name of the property is defined by calling `Root("Root")`. The code of the generated class looks as follows:
```c#
partial class Composition
{
private object _lock = new object();
private Random? _random;
public Program Root
{
get
{
var stateFunc = new Func(() => {
if (_random == null)
lock (_lock)
if (_random == null)
_random = new Random();return (State)_random.Next(2)
});return new Program(
new CardboardBox(
new ShroedingersCat(
new Lazy(
stateFunc))));
}
}
public T Resolve() { ... }public object Resolve(Type type) { ... }
}
```The `public Program Root { get; }` property here is a [*__Composition Root__*](https://blog.ploeh.dk/2011/07/28/CompositionRoot/), the only place in the application where the composition of the object graph for the application takes place. Each instance is created by only basic language constructs, which compiles with all optimizations with minimal impact on performance and memory consumption. In general, applications may have multiple composition roots and thus such properties. Each composition root must have its own unique name, which is defined when the `Root(string name)` method is called, as shown in the above code.
### Time to open boxes!
```c#
class Program(IBox box)
{
// Composition Root, a single place in an application
// where the composition of the object graphs
// for an application take place
static void Main() => new Composition().Root.Run();private void Run() => Console.WriteLine(box);
}
```> [!TIP]
> _Pure.DI_ creates efficient code in a pure DI paradigm, using only basic language constructs as if you were writing code by hand. This allows you to take full advantage of Dependency Injection everywhere and always, without any compromise!The full analog of this application with top-level statements can be found [here](samples/ShroedingersCatTopLevelStatements).
Just try!
Clone a [sample project](https://github.com/DevTeam/Pure.DI.Example):
```shell
git clone https://github.com/DevTeam/Pure.DI.Example.git
```And run it from solution root folder
```shell
cd ./Pure.DI.Example
``````shell
dotnet run
```![](docs/icon.png)
## Examples
### Basics
- [Auto-bindings](readme/auto-bindings.md)
- [Injections of abstractions](readme/injections-of-abstractions.md)
- [Composition roots](readme/composition-roots.md)
- [Resolve methods](readme/resolve-methods.md)
- [Simplified binding](readme/simplified-binding.md)
- [Factory](readme/factory.md)
- [Simplified factory](readme/simplified-factory.md)
- [Class arguments](readme/class-arguments.md)
- [Root arguments](readme/root-arguments.md)
- [Tags](readme/tags.md)
- [Field injection](readme/field-injection.md)
- [Method injection](readme/method-injection.md)
- [Property injection](readme/property-injection.md)
- [Default values](readme/default-values.md)
- [Required properties or fields](readme/required-properties-or-fields.md)
- [Root binding](readme/root-binding.md)
- [Async Root](readme/async-root.md)
### Lifetimes
- [Transient](readme/transient.md)
- [Singleton](readme/singleton.md)
- [PerResolve](readme/perresolve.md)
- [PerBlock](readme/perblock.md)
- [Scope](readme/scope.md)
- [Auto scoped](readme/auto-scoped.md)
- [Default lifetime](readme/default-lifetime.md)
- [Default lifetime for a type](readme/default-lifetime-for-a-type.md)
- [Default lifetime for a type and a tag](readme/default-lifetime-for-a-type-and-a-tag.md)
- [Disposable singleton](readme/disposable-singleton.md)
- [Async disposable singleton](readme/async-disposable-singleton.md)
- [Async disposable scope](readme/async-disposable-scope.md)
### Base Class Library
- [Func](readme/func.md)
- [Enumerable](readme/enumerable.md)
- [Enumerable generics](readme/enumerable-generics.md)
- [Array](readme/array.md)
- [Lazy](readme/lazy.md)
- [Task](readme/task.md)
- [ValueTask](readme/valuetask.md)
- [Manually started tasks](readme/manually-started-tasks.md)
- [Span and ReadOnlySpan](readme/span-and-readonlyspan.md)
- [Tuple](readme/tuple.md)
- [Weak Reference](readme/weak-reference.md)
- [Async Enumerable](readme/async-enumerable.md)
- [Service collection](readme/service-collection.md)
- [Func with arguments](readme/func-with-arguments.md)
- [Func with tag](readme/func-with-tag.md)
- [Keyed service provider](readme/keyed-service-provider.md)
- [Service provider](readme/service-provider.md)
- [Service provider with scope](readme/service-provider-with-scope.md)
- [Overriding the BCL binding](readme/overriding-the-bcl-binding.md)
### Generics
- [Generics](readme/generics.md)
- [Generic composition roots](readme/generic-composition-roots.md)
- [Complex generics](readme/complex-generics.md)
- [Generic composition roots with constraints](readme/generic-composition-roots-with-constraints.md)
- [Generic async composition roots with constraints](readme/generic-async-composition-roots-with-constraints.md)
- [Custom generic argument](readme/custom-generic-argument.md)
### Attributes
- [Constructor ordinal attribute](readme/constructor-ordinal-attribute.md)
- [Member ordinal attribute](readme/member-ordinal-attribute.md)
- [Tag attribute](readme/tag-attribute.md)
- [Type attribute](readme/type-attribute.md)
- [Inject attribute](readme/inject-attribute.md)
- [Custom attributes](readme/custom-attributes.md)
- [Custom universal attribute](readme/custom-universal-attribute.md)
- [Custom generic argument attribute](readme/custom-generic-argument-attribute.md)
- [Bind attribute](readme/bind-attribute.md)
- [Bind attribute with lifetime and tag](readme/bind-attribute-with-lifetime-and-tag.md)
- [Bind attribute for a generic type](readme/bind-attribute-for-a-generic-type.md)
### Interception
- [Decorator](readme/decorator.md)
- [Interception](readme/interception.md)
- [Advanced interception](readme/advanced-interception.md)
### Hints
- [Resolve hint](readme/resolve-hint.md)
- [ThreadSafe hint](readme/threadsafe-hint.md)
- [OnDependencyInjection hint](readme/ondependencyinjection-hint.md)
- [OnCannotResolve hint](readme/oncannotresolve-hint.md)
- [OnNewInstance hint](readme/onnewinstance-hint.md)
- [ToString hint](readme/tostring-hint.md)
- [Check for a root](readme/check-for-a-root.md)
### Advanced
- [Composition root kinds](readme/composition-root-kinds.md)
- [Tag Type](readme/tag-type.md)
- [Tag Unique](readme/tag-unique.md)
- [Tag on injection site](readme/tag-on-injection-site.md)
- [Tag on a constructor argument](readme/tag-on-a-constructor-argument.md)
- [Tag on a member](readme/tag-on-a-member.md)
- [Tag on a method argument](readme/tag-on-a-method-argument.md)
- [Tag on injection site with wildcards](readme/tag-on-injection-site-with-wildcards.md)
- [Dependent compositions](readme/dependent-compositions.md)
- [Accumulators](readme/accumulators.md)
- [Global compositions](readme/global-compositions.md)
- [Partial class](readme/partial-class.md)
- [A few partial classes](readme/a-few-partial-classes.md)
- [Tracking disposable instances per a composition root](readme/tracking-disposable-instances-per-a-composition-root.md)
- [Tracking disposable instances in delegates](readme/tracking-disposable-instances-in-delegates.md)
- [Tracking async disposable instances per a composition root](readme/tracking-async-disposable-instances-per-a-composition-root.md)
- [Tracking async disposable instances in delegates](readme/tracking-async-disposable-instances-in-delegates.md)
- [Exposed roots](readme/exposed-roots.md)
- [Exposed roots with tags](readme/exposed-roots-with-tags.md)
- [Exposed roots via arg](readme/exposed-roots-via-arg.md)
- [Exposed roots via root arg](readme/exposed-roots-via-root-arg.md)
- [Exposed generic roots](readme/exposed-generic-roots.md)
- [Exposed generic roots with args](readme/exposed-generic-roots-with-args.md)
### Applications
- Console
- [Schrödinger's cat](readme/Console.md)
- [Top level statements](readme/ConsoleTopLevelStatements.md)
- [Native AOT](readme/ConsoleNativeAOT.md)
- UI
- [MAUI](readme/Maui.md)
- [WPF](readme/Wpf.md)
- [Avalonia](readme/Avalonia.md)
- [Win Forms Net Core](readme/WinFormsAppNetCore.md)
- [Win Forms](readme/WinFormsApp.md)
- Web
- [Web](readme/WebApp.md)
- [Minimal Web API](readme/MinimalWebAPI.md)
- [Web API](readme/WebAPI.md)
- [gRPC service](readme/GrpcService.md)
- [Blazor Server](readme/BlazorServerApp.md)
- [Blazor WebAssembly](readme/BlazorWebAssemblyApp.md)
- [https://devteam.github.io/Pure.DI/](https://devteam.github.io/Pure.DI/)
- Git repo with examples
- [Schrödinger's cat](https://github.com/DevTeam/Pure.DI.Example)
- [How to use Pure.DI to create and test libraries](https://github.com/DevTeam/Pure.DI.Solution)
## Generated CodeEach generated class, hereafter called a _composition_, must be customized. Setup starts with a call to the `Setup(string compositionTypeName)` method:
```c#
DI.Setup("Composition")
.Bind().To()
.Bind().To()
.Root("Root");
```The following class will be generated
```c#
partial class Composition
{
// Default constructor
public Composition() { }// Scope constructor
internal Composition(Composition baseComposition) { }// Composition root
public IService Root
{
get
{
return new Service(new Dependency());
}
}public T Resolve() { ... }
public T Resolve(object? tag) { ... }
public object Resolve(Type type) { ... }
public object Resolve(Type type, object? tag) { ... }
}
```The _compositionTypeName_ parameter can be omitted
- if the setup is performed inside a partial class, then the composition will be created for this partial class
- for the case of a class with composition kind `CompositionKind.Global`, see [this example](readme/global-compositions.md)Setup arguments
The first parameter is used to specify the name of the composition class. All sets with the same name will be combined to create one composition class. Alternatively, this name may contain a namespace, e.g. a composition class is generated for `Sample.Composition`:
```c#
namespace Sample
{
partial class Composition
{
...
}
}
```The second optional parameter may have multiple values to determine the kind of composition.
### CompositionKind.Public
This value is used by default. If this value is specified, a normal composition class will be created.
### CompositionKind.Internal
If you specify this value, the class will not be generated, but this setup can be used by others as a base setup. For example:
```c#
DI.Setup("BaseComposition", CompositionKind.Internal)
.Bind().To();DI.Setup("Composition").DependsOn("BaseComposition")
.Bind().To();
```If the _CompositionKind.Public_ flag is set in the composition setup, it can also be the base for other compositions, as in the example above.
### CompositionKind.Global
No composition class will be created when this value is specified, but this setup is the base setup for all setups in the current project, and `DependsOn(...)` is not required.
Constructors
### Default constructor
It's quite trivial, this constructor simply initializes the internal state.
### Parameterized constructor
It replaces the default constructor and is only created if at least one argument is specified. For example:
```c#
DI.Setup("Composition")
.Arg("name")
.Arg("id")
...
```In this case, the constructor with arguments is as follows:
```c#
public Composition(string name, int id) { ... }
```and there is no default constructor. It is important to remember that only those arguments that are used in the object graph will appear in the constructor. Arguments that are not involved cannot be defined, as they are omitted from the constructor parameters to save resources.
### Scope constructor
This constructor creates a composition instance for the new scope. This allows ``Lifetime.Scoped`` to be applied. See [this](readme/scope.md) example for details.
Properties
### Public Composition Roots
To create an object graph quickly and conveniently, a set of properties (or a methods) is formed. These properties/methods are here called roots of compositions. The type of a property/method is the type of the root object created by the composition. Accordingly, each invocation of a property/method leads to the creation of a composition with a root element of this type.
```c#
DI.Setup("Composition")
.Bind().To()
.Root("MyService");
```In this case, the property for the _IService_ type will be named _MyService_ and will be available for direct use. The result of its use will be the creation of a composition of objects with the root of _IService_ type:
```c#
public IService MyService
{
get
{
...
return new Service(...);
}
}
```This is [recommended way](https://blog.ploeh.dk/2011/07/28/CompositionRoot/) to create a composition root. A composition class can contain any number of roots.
### Private Composition Roots
If the root name is empty, a private composition root with a random name is created:
```c#
private IService RootM07D16di_0001
{
get { ... }
}
```This root is available in _Resolve_ methods in the same way as public roots. For example:
```c#
DI.Setup("Composition")
.Bind().To()
.Root();
```These properties have an arbitrary name and access modifier _private_ and cannot be used directly from the code. Do not attempt to use them, as their names are arbitrarily changed. Private composition roots can be resolved by _Resolve_ methods.
Methods
### Resolve
By default, a set of four _Resolve_ methods is generated:
```c#
public T Resolve() { ... }public T Resolve(object? tag) { ... }
public object Resolve(Type type) { ... }
public object Resolve(Type type, object? tag) { ... }
```These methods can resolve both public and private composition roots that do not depend on any arguments of the composition roots. They are useful when using the [Service Locator](https://martinfowler.com/articles/injection.html) approach, where the code resolves composition roots in place:
```c#
var composition = new Composition();composition.Resolve();
```This is a [not recommended](https://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/) way to create composition roots because _Resolve_ methods have a number of disadvantages:
- They provide access to an unlimited set of dependencies.
- Their use can potentially lead to runtime exceptions, for example, when the corresponding root has not been defined.
- Lead to performance degradation because they search for the root of a composition based on its type.To control the generation of these methods, see the [Resolve](#resolve-hint) hint.
### Dispose and DisposeAsync
Provides a mechanism to release unmanaged resources. These methods are generated only if the composition contains at least one singleton/scoped instance that implements either the [IDisposable](https://learn.microsoft.com/en-us/dotnet/api/system.idisposable) and/or [DisposeAsync](https://learn.microsoft.com/en-us/dotnet/api/system.iasyncdisposable.disposeasync) interface. The `Dispose()` or `DisposeAsync()` method of the composition should be called to dispose of all created singleton/scoped objects:
```c#
using var composition = new Composition();
```or
```c#
await using var composition = new Composition();
```To dispose objects of other lifetimes please see [this](readme/tracking-disposable-instances-per-a-composition-root.md) or [this](readme/tracking-disposable-instances-in-delegates.md) examples.
Setup hints
## Setup hints
Hints are used to fine-tune code generation. Setup hints can be used as shown in the following example:
```c#
DI.Setup("Composition")
.Hint(Hint.Resolve, "Off")
.Hint(Hint.ThreadSafe, "Off")
.Hint(Hint.ToString, "On")
...
```In addition, setup hints can be commented out before the _Setup_ method as `hint = value`. For example:
```c#
// Resolve = Off
// ThreadSafe = Off
DI.Setup("Composition")
...
```Both approaches can be mixed:
```c#
// Resolve = Off
DI.Setup("Composition")
.Hint(Hint.ThreadSafe, "Off")
...
```| Hint | Values | C# version | Default |
|------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------|------------|-----------|
| [Resolve](#resolve-hint) | _On_ or _Off_ | | _On_ |
| [OnNewInstance](#onnewinstance-hint) | _On_ or _Off_ | 9.0 | _Off_ |
| [OnNewInstancePartial](#onnewinstance-hint) | _On_ or _Off_ | | _On_ |
| [OnNewInstanceImplementationTypeNameRegularExpression](#onnewinstanceimplementationtypenameregularexpression-hint) | Regular expression | | .+ |
| [OnNewInstanceTagRegularExpression](#onnewinstancetagregularexpression-hint) | Regular expression | | .+ |
| [OnNewInstanceLifetimeRegularExpression](#onnewinstancelifetimeregularexpression-hint) | Regular expression | | .+ |
| [OnDependencyInjection](#ondependencyinjection-hint) | _On_ or _Off_ | 9.0 | _Off_ |
| [OnDependencyInjectionPartial](#ondependencyinjectionpartial-hint) | _On_ or _Off_ | | _On_ |
| [OnDependencyInjectionImplementationTypeNameRegularExpression](#OnDependencyInjectionImplementationTypeNameRegularExpression-Hint) | Regular expression | | .+ |
| [OnDependencyInjectionContractTypeNameRegularExpression](#ondependencyinjectioncontracttypenameregularexpression-hint) | Regular expression | | .+ |
| [OnDependencyInjectionTagRegularExpression](#ondependencyinjectiontagregularexpression-hint) | Regular expression | | .+ |
| [OnDependencyInjectionLifetimeRegularExpression](#ondependencyinjectionlifetimeregularexpression-hint) | Regular expression | | .+ |
| [OnCannotResolve](#oncannotresolve-hint) | _On_ or _Off_ | 9.0 | _Off_ |
| [OnCannotResolvePartial](#oncannotresolvepartial-hint) | _On_ or _Off_ | | _On_ |
| [OnCannotResolveContractTypeNameRegularExpression](#oncannotresolvecontracttypenameregularexpression-hint) | Regular expression | | .+ |
| [OnCannotResolveTagRegularExpression](#oncannotresolvetagregularexpression-hint) | Regular expression | | .+ |
| [OnCannotResolveLifetimeRegularExpression](#oncannotresolvelifetimeregularexpression-hint) | Regular expression | | .+ |
| [OnNewRoot](#onnewroot-hint) | _On_ or _Off_ | | _Off_ |
| [OnNewRootPartial](#onnewrootpartial-hint) | _On_ or _Off_ | | _On_ |
| [ToString](#tostring-hint) | _On_ or _Off_ | | _Off_ |
| [ThreadSafe](#threadsafe-hint) | _On_ or _Off_ | | _On_ |
| [ResolveMethodModifiers](#resolvemethodmodifiers-hint) | Method modifier | | _public_ |
| [ResolveMethodName](#resolvemethodname-hint) | Method name | | _Resolve_ |
| [ResolveByTagMethodModifiers](#resolvebytagmethodmodifiers-hint) | Method modifier | | _public_ |
| [ResolveByTagMethodName](#resolvebytagmethodname-hint) | Method name | | _Resolve_ |
| [ObjectResolveMethodModifiers](#objectresolvemethodmodifiers-hint) | Method modifier | | _public_ |
| [ObjectResolveMethodName](#objectresolvemethodname-hint) | Method name | | _Resolve_ |
| [ObjectResolveByTagMethodModifiers](#objectresolvebytagmethodmodifiers-hint) | Method modifier | | _public_ |
| [ObjectResolveByTagMethodName](#objectresolvebytagmethodname-hint) | Method name | | _Resolve_ |
| [DisposeMethodModifiers](#disposemethodmodifiers-hint) | Method modifier | | _public_ |
| [DisposeAsyncMethodModifiers](#disposeasyncmethodmodifiers-hint) | Method modifier | | _public_ |
| [FormatCode](#formatcode-hint) | _On_ or _Off_ | | _Off_ |
| [SeverityOfNotImplementedContract](#severityofnotimplementedcontract-hint) | _Error_ or _Warning_ or _Info_ or _Hidden_ | | _Error_ |
| [Comments](#comments-hint) | _On_ or _Off_ | | _On_ |The list of hints will be gradually expanded to meet the needs and desires for fine-tuning code generation. Please feel free to add your ideas.
### Resolve Hint
Determines whether to generate [_Resolve_ methods](#resolve). By default, a set of four _Resolve_ methods are generated. Set this hint to _Off_ to disable the generation of resolve methods. This will reduce the generation time of the class composition, and in this case no [private composition roots](#private-composition-roots) will be generated. The class composition will be smaller and will only have [public roots](#public-composition-roots). When the _Resolve_ hint is disabled, only the public roots properties are available, so be sure to explicitly define them using the `Root(string name)` method with an explicit composition root name.
### OnNewInstance Hint
Determines whether to use the _OnNewInstance_ partial method. By default, this partial method is not generated. This can be useful, for example, for logging purposes:
```c#
internal partial class Composition
{
partial void OnNewInstance(ref T value, object? tag, object lifetime) =>
Console.WriteLine($"'{typeof(T)}'('{tag}') created.");
}
```You can also replace the created instance with a `T` type, where `T` is the actual type of the created instance. To minimize performance loss when calling _OnNewInstance_, use the three hints below.
### OnNewInstancePartial Hint
Determines whether to generate the _OnNewInstance_ partial method. By default, this partial method is generated when the _OnNewInstance_ hint is ```On```.
### OnNewInstanceImplementationTypeNameRegularExpression Hint
This is a regular expression for filtering by instance type name. This hint is useful when _OnNewInstance_ is in _On_ state and it is necessary to limit the set of types for which the _OnNewInstance_ method will be called.
### OnNewInstanceTagRegularExpression Hint
This is a regular expression for filtering by _tag_. This hint is also useful when _OnNewInstance_ is in _On_ state and it is necessary to limit the set of _tags_ for which the _OnNewInstance_ method will be called.
### OnNewInstanceLifetimeRegularExpression Hint
This is a regular expression for filtering by _lifetime_. This hint is also useful when _OnNewInstance_ is in _On_ state and it is necessary to restrict the set of _life_ times for which the _OnNewInstance_ method will be called.
### OnDependencyInjection Hint
Determines whether to use the _OnDependencyInjection_ partial method when the _OnDependencyInjection_ hint is ```On``` to control dependency injection. By default it is ```On```.
```c#
// OnDependencyInjection = On
// OnDependencyInjectionPartial = Off
// OnDependencyInjectionContractTypeNameRegularExpression = ICalculator[\d]{1}
// OnDependencyInjectionTagRegularExpression = Abc
DI.Setup("Composition")
...
```### OnDependencyInjectionPartial Hint
Determines whether to generate the _OnDependencyInjection_ partial method to control dependency injection. By default, this partial method is not generated. It cannot have an empty body because of the return value. It must be overridden when it is generated. This may be useful, for example, for [Interception Scenario](readme/interception.md).
```c#
// OnDependencyInjection = On
// OnDependencyInjectionContractTypeNameRegularExpression = ICalculator[\d]{1}
// OnDependencyInjectionTagRegularExpression = Abc
DI.Setup("Composition")
...
```To minimize performance loss when calling _OnDependencyInjection_, use the three tips below.
### OnDependencyInjectionImplementationTypeNameRegularExpression Hint
This is a regular expression for filtering by instance type name. This hint is useful when _OnDependencyInjection_ is in _On_ state and it is necessary to restrict the set of types for which the _OnDependencyInjection_ method will be called.
### OnDependencyInjectionContractTypeNameRegularExpression Hint
This is a regular expression for filtering by the name of the resolving type. This hint is also useful when _OnDependencyInjection_ is in _On_ state and it is necessary to limit the set of permissive types for which the _OnDependencyInjection_ method will be called.
### OnDependencyInjectionTagRegularExpression Hint
This is a regular expression for filtering by _tag_. This hint is also useful when _OnDependencyInjection_ is in the _On_ state and you want to limit the set of _tags_ for which the _OnDependencyInjection_ method will be called.
### OnDependencyInjectionLifetimeRegularExpression Hint
This is a regular expression for filtering by _lifetime_. This hint is also useful when _OnDependencyInjection_ is in _On_ state and it is necessary to restrict the set of _lifetime_ for which the _OnDependencyInjection_ method will be called.
### OnCannotResolve Hint
Determines whether to use the `OnCannotResolve(...)` partial method to handle a scenario in which an instance cannot be resolved. By default, this partial method is not generated. Because of the return value, it cannot have an empty body and must be overridden at creation.
```c#
// OnCannotResolve = On
// OnCannotResolveContractTypeNameRegularExpression = string|DateTime
// OnDependencyInjectionTagRegularExpression = null
DI.Setup("Composition")
...
```To avoid missing failed bindings by mistake, use the two relevant hints below.
### OnCannotResolvePartial Hint
Determines whether to generate the `OnCannotResolve(...)` partial method when the _OnCannotResolve_ hint is On to handle a scenario in which an instance cannot be resolved. By default it is ```On```.
```c#
// OnCannotResolve = On
// OnCannotResolvePartial = Off
// OnCannotResolveContractTypeNameRegularExpression = string|DateTime
// OnDependencyInjectionTagRegularExpression = null
DI.Setup("Composition")
...
```To avoid missing failed bindings by mistake, use the two relevant hints below.
### OnNewRoot Hint
Determines whether to use a static partial method `OnNewRoot(...)` to handle the new composition root registration event.
```c#
// OnNewRoot = On
DI.Setup("Composition")
...
```Be careful, this hint disables checks for the ability to resolve dependencies!
### OnNewRootPartial Hint
Determines whether to generate a static partial method `OnNewRoot(...)` when the _OnNewRoot_ hint is ```On``` to handle the new composition root registration event.
```c#
// OnNewRootPartial = Off
DI.Setup("Composition")
...
```### OnCannotResolveContractTypeNameRegularExpression Hint
This is a regular expression for filtering by the name of the resolving type. This hint is also useful when _OnCannotResolve_ is in _On_ state and it is necessary to limit the set of resolving types for which the _OnCannotResolve_ method will be called.
### OnCannotResolveTagRegularExpression Hint
This is a regular expression for filtering by _tag_. This hint is also useful when _OnCannotResolve_ is in _On_ state and it is necessary to limit the set of _tags_ for which the _OnCannotResolve_ method will be called.
### OnCannotResolveLifetimeRegularExpression Hint
This is a regular expression for filtering by _lifetime_. This hint is also useful when _OnCannotResolve_ is in the _On_ state and it is necessary to restrict the set of _lives_ for which the _OnCannotResolve_ method will be called.
### ToString Hint
Determines whether to generate the _ToString()_ method. This method provides a class diagram in [mermaid](https://mermaid.js.org/) format. To see this diagram, just call the ToString method and copy the text to [this site](https://mermaid.live/).
```c#
// ToString = On
DI.Setup("Composition")
.Bind().To()
.Root("MyService");
var composition = new Composition();
string classDiagram = composition.ToString();
```### ThreadSafe Hint
This hint determines whether the composition of objects will be created in a thread-safe way. The default value of this hint is _On_. It is a good practice not to use threads when creating an object graph, in this case the hint can be disabled, which will result in a small performance gain. For example:
```c#
// ThreadSafe = Off
DI.Setup("Composition")
.Bind().To()
.Root("MyService");
```### ResolveMethodModifiers Hint
Overrides the modifiers of the `public T Resolve()` method.
### ResolveMethodName Hint
Overrides the method name for `public T Resolve()`.
### ResolveByTagMethodModifiers Hint
Overrides the modifiers of the `public T Resolve(object? tag)` method.
### ResolveByTagMethodName Hint
Overrides the method name for `public T Resolve(object? tag)`.
### ObjectResolveMethodModifiers Hint
Overrides the modifiers of the `public object Resolve(Type type)` method.
### ObjectResolveMethodName Hint
Overrides the method name for `public object Resolve(Type type)`.
### ObjectResolveByTagMethodModifiers Hint
Overrides the modifiers of the `public object Resolve(Type type, object? tag)` method.
### ObjectResolveByTagMethodName Hint
Overrides the method name for `public object Resolve(Type type, object? tag)`.
### DisposeMethodModifiers Hint
Overrides the modifiers of the `public void Dispose()` method.
### DisposeAsyncMethodModifiers Hint
Overrides the modifiers of the `public ValueTask DisposeAsync()` method.
### FormatCode Hint
Specifies whether the generated code should be formatted. This option consumes a lot of CPU resources. This hint may be useful when studying the generated code or, for example, when making presentations.
### SeverityOfNotImplementedContract Hint
Indicates the severity level of the situation when, in the binding, an implementation does not implement a contract. Possible values:
- _"Error"_, it is default value.
- _"Warning"_ - something suspicious but allowed.
- _"Info"_ - information that does not indicate a problem.
- _"Hidden"_ - what's not a problem.### Comments Hint
Specifies whether the generated code should be commented.
```c#
// Represents the composition class
DI.Setup(nameof(Composition))
.Bind().To()
// Provides a composition root of my service
.Root("MyService");
```Appropriate comments will be added to the generated ```Composition``` class and the documentation for the class, depending on the IDE used, will look something like this:
![ReadmeDocumentation1.png](readme/ReadmeDocumentation1.png)
Then documentation for the composition root:
![ReadmeDocumentation2.png](readme/ReadmeDocumentation2.png)
## NuGet packages
| | | |
|----------------------|---------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------|
| Pure.DI | [![NuGet](https://img.shields.io/nuget/v/Pure.DI)](https://www.nuget.org/packages/Pure.DI) | DI Source code generator |
| Pure.DI.Abstractions | [![NuGet](https://img.shields.io/nuget/v/Pure.DI.Abstractions)](https://www.nuget.org/packages/Pure.DI.Abstractions) | Abstractions for Pure.DI |
| Pure.DI.Templates | [![NuGet](https://img.shields.io/nuget/v/Pure.DI.Templates)](https://www.nuget.org/packages/Pure.DI.Templates) | Template Package you can call from the shell/command line. |
| Pure.DI.MS | [![NuGet](https://img.shields.io/nuget/v/Pure.DI.MS)](https://www.nuget.org/packages/Pure.DI.MS) | Tools for working with Microsoft DI |## Project template
Install the DI template [Pure.DI.Templates](https://www.nuget.org/packages/Pure.DI.Templates)
```shell
dotnet new install Pure.DI.Templates
```Create a "Sample" console application from the template *__di__*
```shell
dotnet new di -o ./Sample
```And run it
```shell
dotnet run --project Sample
```For more information about the template, please see [this page](https://github.com/DevTeam/Pure.DI/wiki/Project-templates).
## Troubleshooting
Version update
When updating the version, it is possible that the previous version of the code generator remains active and is used by compilation services. In this case, the old and new versions of the generator may conflict. For a project where the code generator is used, it is recommended to do the following:
- After updating the version, close the IDE if it is open
- Delete the _obj_ and _bin_ directories
- Execute the following commands one by one```shell
dotnet build-server shutdown
``````shell
dotnet restore
``````shell
dotnet build
```Disabling API generation
_Pure.DI_ automatically generates its API. If an assembly already has the _Pure.DI_ API, for example, from another assembly, it is sometimes necessary to disable its automatic generation to avoid ambiguity. To do this, you need to add a _DefineConstants_ element to the project files of these modules. For example:
```xml
$(DefineConstants);PUREDI_API_SUPPRESSION
```
Display generated files
You can set project properties to save generated files and control their storage location. In the project file, add the `` element to the `` group and set its value to `true`. Build the project again. The generated files are now created in the _obj/Debug/netX.X/generated/Pure.DI/Pure.DI/Pure.DI.SourceGenerator_ directory. The path components correspond to the build configuration, the target framework, the source generator project name, and the full name of the generator type. You can choose a more convenient output folder by adding the `` element to the application project file. For example:
```xml
true
$(BaseIntermediateOutputPath)Generated
```
## Contribution
Thank you for your interest in contributing to the _Pure.DI_ project! First of all, if you are going to make a big change or feature, please open a problem first. That way, we can coordinate and understand if the change you're going to work on fits with current priorities and if we can commit to reviewing and merging it within a reasonable timeframe. We don't want you to waste a lot of your valuable time on something that may not align with what we want for _Pure.DI_.
The entire build logic is a regular [console .NET application](/build). You can use the [build.cmd](/build.cmd) and [build.sh](/build.sh) files with the appropriate command in the parameters to perform all basic actions on the project, e.g:
| Command | Description |
|----------------|-------------------------------------------------------------------|
| g, generator | Builds and tests generator |
| l, libs | Builds and tests libraries |
| c, check | Compatibility checks |
| p, pack | Creates NuGet packages |
| r, readme | Generates README.md |
| benchmarks, bm | Runs benchmarks |
| deploy, dp | Deploys packages |
| t, template | Creates and deploys templates |
| u, upgrade | Upgrading the internal version of DI to the latest public version |For example:
```
./build.sh pack
./build.cmd benchmarks
```If you are using the Rider IDE, it already has a set of configurations to run these commands.
> [!TIP]
> This project uses [C# interactive](https://github.com/DevTeam/csharp-interactive) build automation system for .NET
>
> ![](https://raw.githubusercontent.com/DevTeam/csharp-interactive/master/docs/icon.ico)
>
> This tool helps to make .NET builds more efficient.Contribution Prerequisites:
Installed [.NET SDK 8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
### Additional resources
> [C# interactive](https://github.com/DevTeam/csharp-interactive) build automation system for .NET
Examples of how to set up a composition
- [Pure.DI](https://github.com/DevTeam/Pure.DI/blob/master/src/Pure.DI.Core/Generator.cs)
- [C# interactive](https://github.com/DevTeam/csharp-interactive/blob/master/CSharpInteractive/Composition.cs)
- [Immutype](https://github.com/DevTeam/Immutype/blob/master/Immutype/Composition.cs)
- [MSBuild logger](https://github.com/JetBrains/teamcity-msbuild-logger/blob/master/TeamCity.MSBuild.Logger/Composition.cs)Articles
- [RU New in Pure.DI](https://habr.com/ru/articles/808297/)
- [RU Pure.DI v2.1](https://habr.com/ru/articles/795809/)
- [RU Pure.DI next step](https://habr.com/ru/articles/554236/)
- [RU Pure.DI for .NET](https://habr.com/ru/articles/552858/)## Benchmarks
Array
Method Mean ErrorStdDevRatioRatioSDGen0Gen1AllocatedAlloc Ratio
'Hand Coded'168.1 ns1.72 ns1.44 ns1.000.000.0336-632 B1.00
'Pure.DI composition root'169.6 ns3.08 ns2.58 ns1.010.020.0336-632 B1.00
'Pure.DI Resolve<T>()'170.7 ns3.02 ns2.52 ns1.020.020.0336-632 B1.00
'Pure.DI Resolve(Type)'171.2 ns2.44 ns2.04 ns1.020.020.0336-632 B1.00
LightInject179.8 ns3.43 ns3.21 ns1.070.020.0336-632 B1.00
DryIoc200.5 ns3.89 ns4.16 ns1.200.020.0336-632 B1.00
Unity9,966.0 ns80.16 ns62.58 ns59.260.420.7629-14520 B22.97
Autofac27,170.5 ns308.31 ns257.45 ns161.601.871.52590.061028816 B45.59[Array details](readme/ArrayDetails.md)
Enum
Method Mean ErrorStdDevRatioRatioSDGen0Gen1AllocatedAlloc Ratio
'Pure.DI composition root'128.2 ns1.50 ns1.25 ns0.910.010.0184-344 B1.00
'Pure.DI Resolve(Type)'130.5 ns2.22 ns1.85 ns0.930.020.0184-344 B1.00
'Pure.DI Resolve<T>()'133.8 ns2.38 ns1.99 ns0.950.010.0184-344 B1.00
'Hand Coded'140.3 ns1.69 ns1.41 ns1.000.000.0184-344 B1.00
'Microsoft DI'181.3 ns2.01 ns1.67 ns1.290.020.0250-472 B1.37
LightInject277.7 ns3.47 ns2.89 ns1.980.030.0458-856 B2.49
DryIoc281.7 ns5.33 ns4.45 ns2.010.030.0458-856 B2.49
Unity7,768.1 ns124.10 ns152.41 ns55.431.240.7324-13752 B39.98
Autofac26,954.8 ns529.73 ns543.99 ns192.634.871.52590.061028944 B84.14[Enum details](readme/EnumDetails.md)
Func
Method Mean ErrorStdDevRatioRatioSDGen0AllocatedAlloc Ratio
'Pure.DI composition root'8.002 ns0.1496 ns0.1326 ns0.850.010.001324 B1.00
'Hand Coded'9.445 ns0.1727 ns0.1615 ns1.000.000.001324 B1.00
'Pure.DI Resolve<T>()'10.327 ns0.2801 ns0.2620 ns1.090.020.001324 B1.00
'Pure.DI Resolve(Type)'11.954 ns0.2458 ns0.2053 ns1.260.030.001324 B1.00
DryIoc62.785 ns1.1086 ns0.9828 ns6.650.190.0063120 B5.00
LightInject296.638 ns3.9363 ns3.2870 ns31.370.800.0267504 B21.00
Unity4,448.473 ns35.3598 ns33.0756 ns471.119.330.12972552 B106.33
Autofac10,857.806 ns115.9508 ns96.8241 ns1,147.9522.400.747714008 B583.67[Func details](readme/FuncDetails.md)
Singleton
Method Mean Error StdDev RatioRatioSDGen0Gen1AllocatedAlloc Ratio
'Hand Coded'7.600 ns0.1685 ns0.1316 ns1.000.000.0013-24 B1.00
'Pure.DI composition root'8.652 ns0.2436 ns0.2392 ns1.140.030.0013-24 B1.00
'Pure.DI Resolve<T>()'9.919 ns0.2579 ns0.2154 ns1.300.040.0013-24 B1.00
'Pure.DI Resolve(Type)'11.393 ns0.2199 ns0.1836 ns1.500.040.0013-24 B1.00
DryIoc27.581 ns0.3758 ns0.3138 ns3.630.070.0013-24 B1.00
'Simple Injector'33.807 ns0.3441 ns0.3050 ns4.450.100.0013-24 B1.00
'Microsoft DI'38.478 ns0.3053 ns0.2549 ns5.060.070.0013-24 B1.00
LightInject865.775 ns1.2567 ns1.0494 ns113.961.960.0010-24 B1.00
Unity7,677.459 ns99.8336 ns88.4999 ns1,011.6717.400.1678-3184 B132.67
Autofac18,825.341 ns315.1815 ns263.1908 ns2,478.0062.671.28170.030524208 B1,008.67
'Castle Windsor'31,318.225 ns408.7010 ns362.3028 ns4,123.5674.241.2207-23912 B996.33
Ninject116,099.082 ns2,240.6371 ns2,991.1861 ns15,609.54360.853.90630.976674096 B3,087.33[Singleton details](readme/SingletonDetails.md)
Transient
Method Mean Error StdDev RatioRatioSDGen0Gen1AllocatedAlloc Ratio
'Pure.DI composition root'7.903 ns0.1254 ns0.0979 ns0.970.020.0013-24 B1.00
'Hand Coded'8.135 ns0.1484 ns0.1158 ns1.000.000.0013-24 B1.00
'Pure.DI Resolve<T>()'10.337 ns0.2882 ns0.3848 ns1.280.040.0013-24 B1.00
'Pure.DI Resolve(Type)'11.442 ns0.2251 ns0.1995 ns1.410.030.0013-24 B1.00
LightInject20.096 ns0.0607 ns0.0507 ns2.470.040.0013-24 B1.00
'Microsoft DI'25.671 ns0.1087 ns0.0849 ns3.160.050.0013-24 B1.00
DryIoc27.474 ns0.5736 ns0.4478 ns3.380.070.0013-24 B1.00
'Simple Injector'34.814 ns0.2384 ns0.2114 ns4.280.050.0013-24 B1.00
Unity11,090.606 ns65.9897 ns61.7268 ns1,366.0819.490.2747-5176 B215.67
Autofac28,646.903 ns355.7947 ns315.4027 ns3,520.2461.911.77000.091633224 B1,384.33
'Castle Windsor'58,534.573 ns988.3103 ns876.1114 ns7,196.49162.412.8687-54360 B2,265.00
Ninject253,820.129 ns4,777.4163 ns5,310.0855 ns31,080.38673.816.83591.4648131008 B5,458.67[Transient details](readme/TransientDetails.md)
Benchmarks environment
BenchmarkDotNet v0.13.12, Ubuntu 20.04.6 LTS (Focal Fossa)
Intel Xeon Platinum 8259CL CPU 2.50GHz, 1 CPU, 2 logical cores and 1 physical core
.NET SDK 8.0.201
[Host] : .NET 8.0.2 (8.0.224.6711), X64 RyuJIT AVX-512F+CD+BW+DQ+VL
DefaultJob : .NET 8.0.2 (8.0.224.6711), X64 RyuJIT AVX-512F+CD+BW+DQ+VL