Skip to content

Commit

Permalink
Update README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
NikolayPianikov committed Jul 23, 2024
1 parent 27a1ccd commit b36e0d2
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 44 deletions.
2 changes: 2 additions & 0 deletions Pure.DI.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=dependencyinjection/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Enumerables/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=hild/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=immutype/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=inheritdoc/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=initializable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=initializables/@EntryIndexedValue">True</s:Boolean>
Expand All @@ -289,6 +290,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pianikov/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pinnable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=precompiling/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=puredi/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=randomizer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=reentrancy/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Rewriter/@EntryIndexedValue">True</s:Boolean>
Expand Down
73 changes: 52 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ DI.Setup(nameof(Composition))
.Root<Program>("Root");
```

> [!NOTE]
> In fact, the `Bind().As(Singleton).To<Random>()` 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<Program>("Root")`. The code of the generated class looks as follows:

```c#
Expand Down Expand Up @@ -387,10 +390,10 @@ If you specify this value, the class will not be generated, but this setup can b

```c#
DI.Setup("BaseComposition", CompositionKind.Internal)
.Bind<IDependency>().To<Dependency>();
.Bind().To<Dependency>();

DI.Setup("Composition").DependsOn("BaseComposition")
.Bind<IService>().To<Service>();
.Bind().To<Service>();
```

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.
Expand Down Expand Up @@ -425,10 +428,7 @@ In this case, the constructor with arguments is as follows:
public Composition(string name, int id) { ... }
```

and there is no default constructor.

> [!IMPORTANT]
> 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.
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

Expand All @@ -441,7 +441,7 @@ This constructor creates a composition instance for the new scope. This allows `

### Public Composition Roots

To create an object graph quickly and conveniently, a set of properties (or a methods) is formed. These properties 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.
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")
Expand Down Expand Up @@ -512,16 +512,29 @@ var composition = new Composition();
composition.Resolve<IService>();
```

This is a [not recommended](https://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/) way to create composition roots. To control the generation of these methods, see the [Resolve](#resolve-hint) hint.
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.

### Dispose
To control the generation of these methods, see the [Resolve](#resolve-hint) hint.

Provides a mechanism to release unmanaged resources. This method is generated only if the composition contains at least one singleton instance that implements the [IDisposable](https://learn.microsoft.com/en-us/dotnet/api/system.idisposable) interface. To dispose of all created singleton objects, the `Dispose()` method of the composition should be called:
### 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.

</details>

<details>
Expand All @@ -545,11 +558,17 @@ In addition, setup hints can be commented out before the _Setup_ method as `hint
// Resolve = Off
// ThreadSafe = Off
DI.Setup("Composition")
.Hint(Hint.ToString, "On")
...
```

Both approaches can be used in combination with each other.
Both approaches can be mixed:

```c#
// Resolve = Off
DI.Setup("Composition")
.Hint(Hint.ThreadSafe, "Off")
...
```

| Hint | Values | C# version | Default |
|------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------|------------|-----------|
Expand Down Expand Up @@ -601,10 +620,8 @@ Determines whether to use the _OnNewInstance_ partial method. By default, this p
```c#
internal partial class Composition
{
partial void OnNewInstance<T>(ref T value, object? tag, object lifetime)
{
Console.WriteLine($"'{typeof(T)}'('{tag}') created.");
}
partial void OnNewInstance<T>(ref T value, object? tag, object lifetime) =>
Console.WriteLine($"'{typeof(T)}'('{tag}') created.");
}
```

Expand Down Expand Up @@ -952,17 +969,31 @@ If you are using the Rider IDE, it already has a set of configurations to run th
>
> This tool helps to make .NET builds more efficient.
### Contribution Prerequisites
Contribution Prerequisites:

Installed [.NET SDK 8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)

### Additional resources:

DotNext video
### Additional resources

<a href="http://www.youtube.com/watch?feature=player_embedded&v=nrp9SH-gLqg" target="_blank"><img src="http://img.youtube.com/vi/nrp9SH-gLqg/0.jpg"
> RU DotNext video
>
> <a href="http://www.youtube.com/watch?feature=player_embedded&v=nrp9SH-gLqg" target="_blank"><img src="http://img.youtube.com/vi/nrp9SH-gLqg/0.jpg"
alt="DotNext Pure.DI" width="640" border="10"/></a>
> [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.Composition.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

Expand Down
70 changes: 49 additions & 21 deletions readme/FooterTemplate.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ If you specify this value, the class will not be generated, but this setup can b

```c#
DI.Setup("BaseComposition", CompositionKind.Internal)
.Bind<IDependency>().To<Dependency>();
.Bind().To<Dependency>();

DI.Setup("Composition").DependsOn("BaseComposition")
.Bind<IService>().To<Service>();
.Bind().To<Service>();
```

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.
Expand Down Expand Up @@ -134,10 +134,7 @@ In this case, the constructor with arguments is as follows:
public Composition(string name, int id) { ... }
```

and there is no default constructor.

> [!IMPORTANT]
> 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.
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

Expand All @@ -150,7 +147,7 @@ This constructor creates a composition instance for the new scope. This allows `

### Public Composition Roots

To create an object graph quickly and conveniently, a set of properties (or a methods) is formed. These properties 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.
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")
Expand Down Expand Up @@ -221,16 +218,29 @@ var composition = new Composition();
composition.Resolve<IService>();
```

This is a [not recommended](https://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/) way to create composition roots. To control the generation of these methods, see the [Resolve](#resolve-hint) hint.
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
### Dispose and DisposeAsync

Provides a mechanism to release unmanaged resources. This method is generated only if the composition contains at least one singleton instance that implements the [IDisposable](https://learn.microsoft.com/en-us/dotnet/api/system.idisposable) interface. To dispose of all created singleton objects, the `Dispose()` method of the composition should be called:
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.

</details>

<details>
Expand All @@ -254,11 +264,17 @@ In addition, setup hints can be commented out before the _Setup_ method as `hint
// Resolve = Off
// ThreadSafe = Off
DI.Setup("Composition")
.Hint(Hint.ToString, "On")
...
```

Both approaches can be used in combination with each other.
Both approaches can be mixed:

```c#
// Resolve = Off
DI.Setup("Composition")
.Hint(Hint.ThreadSafe, "Off")
...
```

| Hint | Values | C# version | Default |
|------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------|------------|-----------|
Expand Down Expand Up @@ -310,10 +326,8 @@ Determines whether to use the _OnNewInstance_ partial method. By default, this p
```c#
internal partial class Composition
{
partial void OnNewInstance<T>(ref T value, object? tag, object lifetime)
{
Console.WriteLine($"'{typeof(T)}'('{tag}') created.");
}
partial void OnNewInstance<T>(ref T value, object? tag, object lifetime) =>
Console.WriteLine($"'{typeof(T)}'('{tag}') created.");
}
```

Expand Down Expand Up @@ -661,13 +675,27 @@ If you are using the Rider IDE, it already has a set of configurations to run th
>
> This tool helps to make .NET builds more efficient.
### Contribution Prerequisites
Contribution Prerequisites:

Installed [.NET SDK 8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)

### Additional resources:

DotNext video
### Additional resources

<a href="http://www.youtube.com/watch?feature=player_embedded&v=nrp9SH-gLqg" target="_blank"><img src="http://img.youtube.com/vi/nrp9SH-gLqg/0.jpg"
> RU DotNext video
>
> <a href="http://www.youtube.com/watch?feature=player_embedded&v=nrp9SH-gLqg" target="_blank"><img src="http://img.youtube.com/vi/nrp9SH-gLqg/0.jpg"
alt="DotNext Pure.DI" width="640" border="10"/></a>
> [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.Composition.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/)
3 changes: 3 additions & 0 deletions readme/ReadmeTemplate.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ DI.Setup(nameof(Composition))
.Root<Program>("Root");
```

> [!NOTE]
> In fact, the `Bind().As(Singleton).To<Random>()` 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<Program>("Root")`. The code of the generated class looks as follows:

```c#
Expand Down
63 changes: 62 additions & 1 deletion readme/tag-on-a-constructor-argument.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The wildcards ‘*’ and ‘?’ are supported.


```c#
namespace Pure.DI.UsageTests.Advanced.OnConstructorArgScenario;
namespace Pure.DI.UsageTests.Advanced.TagOnConstructorArgScenario;


interface IDependency;
Expand Down Expand Up @@ -56,5 +56,66 @@ service.Dependency2.ShouldBeOfType<XyzDependency>();
> [!WARNING]
> Each potentially injectable argument, property, or field contains an additional tag. This tag can be used to specify what can be injected there. This will only work if the binding type and the tag match. So while this approach can be useful for specifying what to enter, it can be more expensive to maintain and less reliable, so it is recommended to use attributes like `[Tag(...)]` instead.
The following partial class will be generated:

```c#
partial class Composition
{
private readonly Composition _root;

public Composition()
{
_root = this;
}

internal Composition(Composition parentScope)
{
_root = (parentScope ?? throw new ArgumentNullException(nameof(parentScope)))._root;
}

public IService Root
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return new Service(new AbcDependency(), new Consumer<string>(new XyzDependency()));
}
}
}
```

Class diagram:

```mermaid
classDiagram
class Composition {
<<partial>>
+IService Root
}
class ConsumerᐸStringᐳ {
+Consumer(IDependency myDep)
}
AbcDependency --|> IDependency
class AbcDependency {
+AbcDependency()
}
XyzDependency --|> IDependency
class XyzDependency {
+XyzDependency()
}
Service --|> IService
class Service {
+Service(IDependency dependency1, ConsumerᐸStringᐳ consumer)
}
class IDependency {
<<interface>>
}
class IService {
<<interface>>
}
ConsumerᐸStringᐳ *-- XyzDependency : IDependency
Composition ..> Service : IService Root
Service *-- AbcDependency : IDependency
Service *-- ConsumerᐸStringᐳ : ConsumerᐸStringᐳ
```

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
// ReSharper disable UnusedTypeParameter
#pragma warning disable CS9113 // Parameter is unread.
// {
namespace Pure.DI.UsageTests.Advanced.OnConstructorArgScenario;
namespace Pure.DI.UsageTests.Advanced.TagOnConstructorArgScenario;

// }

Expand Down

0 comments on commit b36e0d2

Please sign in to comment.