diff --git a/CHANGELOG.md b/CHANGELOG.md
index fb33d48a..be69ee76 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [v5.13.0] - 2023-11-18
+### Added
+- .NET 8.0 target.
+- [#134](https://github.com/z4kn4fein/stashbox/issues/134) Concept of [Auto lifetime](https://z4kn4fein.github.io/stashbox/docs/guides/lifetimes#auto-lifetime):
+ - It aligns to the lifetime of the resolved service's dependencies. When the underlying service has a dependency with a higher lifespan, this lifetime will inherit that lifespan up to a given boundary.
+- Auto injection of `required` members.
+- MS.DI compatibility features for supporting [keyed services](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8#keyed-di-services):
+ - `DependencyName` attribute. When a parameter is marked with this attribute, the container will pass the given dependency's name to it.
+ - `WithUniversalName()` container configuration method. It sets the universal name which is a special name that allows named resolution work for any given name.
+ - `WithAdditionalDependencyNameAttribute()` container configuration method. It adds an attribute type that is considered a dependency name indicator just like the [`DependencyName` attribute](https://z4kn4fein.github.io/stashbox/docs/guides/service-resolution#attributes).
+ - `WithAdditionalDependencyAttribute()` container configuration method. It adds an attribute type that is considered a dependency indicator just like the [`Dependency` attribute](https://z4kn4fein.github.io/stashbox/docs/guides/service-resolution#attributes).
+
## [v5.12.2] - 2023-09-05
### Fixed
- There was an issue where using decorators with instance registrations resulted in resolution failure.
@@ -398,6 +410,7 @@ The validation was executed only at the expression tree building phase, so an al
- Removed the legacy container extension functionality.
- Removed the support of PCL v259.
+[v5.13.0]: https://github.com/z4kn4fein/stashbox/compare/5.12.2...5.13.0
[v5.12.2]: https://github.com/z4kn4fein/stashbox/compare/5.12.1...5.12.2
[v5.12.1]: https://github.com/z4kn4fein/stashbox/compare/5.11.1...5.12.1
[v5.11.1]: https://github.com/z4kn4fein/stashbox/compare/5.11.0...5.11.1
diff --git a/README.md b/README.md
index 43ed2b96..97fe8c51 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ Stashbox is a lightweight, fast, and portable dependency injection framework for
Github (stable) | NuGet (stable) | Fuget (stable) | NuGet (pre-release)
--- | --- |---------------------------------------------------------------------------------------------------------------------------------| ---
-[![Github release](https://img.shields.io/github/release/z4kn4fein/stashbox.svg)](https://github.com/z4kn4fein/stashbox/releases) | [![NuGet Version](https://buildstats.info/nuget/Stashbox)](https://www.nuget.org/packages/Stashbox/) | [![Stashbox on fuget.org](https://www.fuget.org/packages/Stashbox/badge.svg?v=5.12.2)](https://www.fuget.org/packages/Stashbox) | [![Nuget pre-release](https://img.shields.io/nuget/vpre/Stashbox)](https://www.nuget.org/packages/Stashbox/)
+[![Github release](https://img.shields.io/github/release/z4kn4fein/stashbox.svg)](https://github.com/z4kn4fein/stashbox/releases) | [![NuGet Version](https://buildstats.info/nuget/Stashbox)](https://www.nuget.org/packages/Stashbox/) | [![Stashbox on fuget.org](https://www.fuget.org/packages/Stashbox/badge.svg?v=5.13.0)](https://www.fuget.org/packages/Stashbox) | [![Nuget pre-release](https://img.shields.io/nuget/vpre/Stashbox)](https://www.nuget.org/packages/Stashbox/)
## Core Attributes
- 🚀 Fast, thread-safe, and lock-free operations.
diff --git a/docs/docs/configuration/container-configuration.md b/docs/docs/configuration/container-configuration.md
index a7d032cd..86c57224 100644
--- a/docs/docs/configuration/container-configuration.md
+++ b/docs/docs/configuration/container-configuration.md
@@ -68,6 +68,7 @@ new StashboxContainer(options => options
## Auto member-injection
With this option, you can enable or disable the auto member-injection without [attributes](/docs/guides/service-resolution#attributes).
+
@@ -140,9 +141,26 @@ new StashboxContainer(options => options
+
+
-:::note
-Member selection filter: `config.WithAutoMemberInjection(filter: member => member.Type != typeof(IJob))`
+#### Member selection filter
+You can pass your own member selection logic to control which members should be auto injected.
+
+
+
+
+```cs
+new StashboxContainer(options => options
+ .WithAutoMemberInjection(
+ filter: member => member.Type != typeof(ILogger)));
+```
+
+
+
+
+:::info
+Members defined with C# 11's [`required`](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required) keyword are automatically injected by the container.
:::
## Constructor selection
@@ -328,6 +346,57 @@ new StashboxContainer(options => options
+## Named service resolution
+
+
+
+### `WithUniversalName`
+Sets the universal name that represents a special name which allows named resolution work for any given name.
+
+
+
+
+```cs
+new StashboxContainer(options => options
+ .WithUniversalName("Any"));
+```
+
+
+
+
+
+
+
+### `WithAdditionalDependencyNameAttribute`
+Adds an attribute type that is considered a dependency name indicator just like the [`DependencyName` attribute](/docs/guides/service-resolution#attributes).
+
+
+
+
+```cs
+new StashboxContainer(options => options
+ .WithAdditionalDependencyNameAttribute());
+```
+
+
+
+
+
+
+
+### `WithAdditionalDependencyAttribute`
+Adds an attribute type that is considered a dependency indicator just like the [`Dependency` attribute](/docs/guides/service-resolution#attributes).
+
+
+
+
+```cs
+new StashboxContainer(options => options
+ .WithAdditionalDependencyAttribute());
+```
+
+
+
## Default value injection
diff --git a/docs/docs/configuration/registration-configuration.md b/docs/docs/configuration/registration-configuration.md
index 07c94e66..f7737ce7 100644
--- a/docs/docs/configuration/registration-configuration.md
+++ b/docs/docs/configuration/registration-configuration.md
@@ -374,7 +374,7 @@ container.Register(config => config
### `WithPerScopedRequestLifetime`
-Sets the lifetime to `PerScopedRequestLifetime`. That means this registration will behave like a singleton within every scoped resolution request.
+Sets the lifetime to `PerScopedRequestLifetime`. This lifetime will create a new instance between scoped services. This means that every scoped service will get a different instance but within their dependency tree it will behave as a singleton.
@@ -390,6 +390,40 @@ container.Register
(options => options
+### `WithPerRequestLifetime`
+Sets the lifetime to `PerRequestLifetime`. This lifetime will create a new instance between resolution requests. Within the request the same instance will be re-used.
+
+
+
+
+```cs
+container.Register(options => options
+ .WithPerRequestLifetime());
+```
+
+
+
+
+
+
+
+### `WithAutoLifetime`
+Sets the lifetime to auto lifetime. This lifetime aligns to the lifetime of the resolved service's dependencies. When the underlying service has a dependency with a higher lifespan, this lifetime will inherit that lifespan up to a given boundary.
+
+
+
+
+```cs
+container.Register(options => options
+ .WithAutoLifetime(Lifetimes.Scoped /* boundary lifetime */));
+```
+
+
+
+
+
+
+
### `WithLifetime`
Sets a custom lifetime for the registration.
@@ -708,7 +742,9 @@ container.Register(options => options
-
+:::info
+Members defined with C# 11's [`required`](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required) keyword are automatically injected by the container.
+:::
## Injection parameters
diff --git a/docs/docs/getting-started/introduction.md b/docs/docs/getting-started/introduction.md
index 4ae34da2..52b46efa 100644
--- a/docs/docs/getting-started/introduction.md
+++ b/docs/docs/getting-started/introduction.md
@@ -12,7 +12,7 @@ Stashbox and its extensions are distributed via [NuGet](https://www.nuget.org/pa
You can install the package by typing the following into the Package Manager Console:
```powershell
-Install-Package Stashbox -Version 5.12.2
+Install-Package Stashbox -Version 5.13.0
```
@@ -20,7 +20,7 @@ Install-Package Stashbox -Version 5.12.2
You can install the package by using the dotnet cli:
```bash
-dotnet add package Stashbox --version 5.12.2
+dotnet add package Stashbox --version 5.13.0
```
@@ -28,7 +28,7 @@ dotnet add package Stashbox --version 5.12.2
You can add the package into the package references of your `.csproj`:
```xml
-
+
```
diff --git a/docs/docs/getting-started/overview.md b/docs/docs/getting-started/overview.md
index ea82b8c8..f049c349 100644
--- a/docs/docs/getting-started/overview.md
+++ b/docs/docs/getting-started/overview.md
@@ -17,7 +17,7 @@ These are the latest available stable and pre-release versions:
Github (stable) | NuGet (stable) | Fuget (stable) | NuGet (daily)
--- | --- |---------------------------------------------------------------------------------------------------------------------------------| ---
-[![Github release](https://img.shields.io/github/release/z4kn4fein/stashbox.svg)](https://github.com/z4kn4fein/stashbox/releases) | [![NuGet Version](https://buildstats.info/nuget/Stashbox)](https://www.nuget.org/packages/Stashbox/) | [![Stashbox on fuget.org](https://www.fuget.org/packages/Stashbox/badge.svg?v=5.12.2)](https://www.fuget.org/packages/Stashbox) | [![Nuget pre-release](https://img.shields.io/nuget/vpre/Stashbox)](https://www.nuget.org/packages/Stashbox/)
+[![Github release](https://img.shields.io/github/release/z4kn4fein/stashbox.svg)](https://github.com/z4kn4fein/stashbox/releases) | [![NuGet Version](https://buildstats.info/nuget/Stashbox)](https://www.nuget.org/packages/Stashbox/) | [![Stashbox on fuget.org](https://www.fuget.org/packages/Stashbox/badge.svg?v=5.13.0)](https://www.fuget.org/packages/Stashbox) | [![Nuget pre-release](https://img.shields.io/nuget/vpre/Stashbox)](https://www.nuget.org/packages/Stashbox/)
## Core attributes
- 🚀 Fast, thread-safe, and lock-free operations.
diff --git a/docs/docs/guides/lifetimes.md b/docs/docs/guides/lifetimes.md
index e57974ef..2f73fd7a 100644
--- a/docs/docs/guides/lifetimes.md
+++ b/docs/docs/guides/lifetimes.md
@@ -156,10 +156,6 @@ It is the same as scoped lifetime, except the given service will be selected onl
You can also let a service [define](/docs/guides/scopes#service-as-scope) its own named scope. During registration, this scope can be referred to by its name upon using a named scope lifetime.
-:::note
-Services with named scope lifetime are disposed when the related named scope is being disposed.
-:::
-
@@ -213,6 +209,10 @@ DbJobExecutor executor = scope.Resolve();
+:::note
+Services with named scope lifetime are disposed when the related named scope is being disposed.
+:::
+
## Per-request lifetime
@@ -249,6 +249,66 @@ container.Register(options => options
+## Auto lifetime
+
+
+
+
+The requested service's lifetime will align to the lifetime of its dependencies. When the requested service has a dependency with a higher lifespan, this lifetime will inherit that lifespan up to a given boundary.
+
+
+
+
+```cs
+container.Register(options => options
+ .WithAutoLifetime(Lifetimes.Scoped /* boundary lifetime */));
+```
+
+
+
+
+
+
+
+If the requested service has auto lifetime with a scoped boundary and it has only transient dependencies, it'll inherit their transient lifetime.
+
+
+
+
+```cs
+container.Register();
+
+container.Register(options => options
+ .WithAutoLifetime(Lifetimes.Scoped /* boundary lifetime */));
+
+// job has transient lifetime.
+var job = container.Resolve();
+```
+
+
+
+
+
+
+
+When there's a dependency with higher lifespan than the given boundary, the requested service will get the boundary lifetime.
+
+
+
+
+```cs
+container.RegisterSingleton();
+
+container.Register(options => options
+ .WithAutoLifetime(Lifetimes.Scoped /* boundary lifetime */));
+
+// job has scoped lifetime.
+var job = container.Resolve();
+```
+
+
+
+
## Custom lifetime
If you'd like to use a custom lifetime, you can create your implementation by inheriting either from `FactoryLifetimeDescriptor` or from `ExpressionLifetimeDescriptor`, depending on how do you want to manage the service instances.
diff --git a/docs/docs/guides/service-resolution.md b/docs/docs/guides/service-resolution.md
index 1d11898f..f4b0653d 100644
--- a/docs/docs/guides/service-resolution.md
+++ b/docs/docs/guides/service-resolution.md
@@ -19,6 +19,8 @@ Stashbox, by default, uses the constructor that has the most parameters it knows
[Property/field injection](/docs/configuration/registration-configuration#property-field-injection) is also supported in cases where constructor injection is not applicable.
+Members defined with C# 11's [`required`](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required) keyword are automatically injected by the container.
+
:::info
[Constructor selection](/docs/configuration/container-configuration#constructor-selection) and [property/field injection](/docs/configuration/container-configuration#auto-member-injection) is also configurable container-wide.
:::
@@ -97,6 +99,8 @@ Attributes can give you control over how Stashbox selects dependencies for a ser
- **On a property/field**: first, it enables *auto-injection* on the marked property/field (even if it wasn't configured at registration explicitly), and just as with the method parameter, it allows [named resolution](/docs/getting-started/glossary#named-resolution).
+**DependencyName attribute**: marks a parameter to let the container know that it must pass the given dependency's name to it.
+
**InjectionMethod attribute**: marks a method to be called when the requested service is instantiated.
@@ -147,6 +151,24 @@ container.Register();
IJob job = container.Resolve();
```
+
+
+
+```cs
+class DbBackup : IJob
+{
+ public string Name { get; set; }
+
+ public DbBackup([DependencyName] string name)
+ { }
+}
+
+container.Register("Backup");
+
+// job.Name is "Backup".
+IJob job = container.Resolve();
+```
+
@@ -176,19 +198,84 @@ IJob job = container.Resolve();
:::caution
-Attributes provide a more straightforward configuration, but using them also tightens the bond between your application and Stashbox. If you consider this an issue, the same functionality is available on the *registration API* as [dependency binding](/docs/guides/service-resolution#dependency-binding).
+Attributes provide a more straightforward configuration, but using them also tightens the bond between your application and Stashbox. If you consider this an issue, you can use the [dependency binding](/docs/guides/service-resolution#dependency-binding) API or [your own attributes](/docs/guides/service-resolution#using-your-own-attributes).
:::
+### Using your own attributes
+
+
+
+There's an option to extend the container's dependency finding mechanism with your own attributes.
+
+- **Additional Dependency attributes**: you can use the [`.WithAdditionalDependencyAttribute()`](/docs/configuration/container-configuration#withadditionaldependencyattribute) container configuration option to let the container know that it should watch for additional attributes besides the built-in [`Dependency`](/docs/guides/service-resolution#attributes) attribute upon building up the [resolution tree](/docs/getting-started/glossary#resolution-tree).
+
+- **Additional DependencyName attributes**: you can use the [`.WithAdditionalDependencyNameAttribute()`](/docs/configuration/container-configuration#withadditionaldependencynameattribute) container configuration option to use additional dependency name indicator attributes besides the built-in [`DependencyName`](/docs/guides/service-resolution#attributes) attribute.
+
+
+
+
+
+
+
+```cs
+class DbBackup : IJob
+{
+ [CustomDependency("Console")]
+ public ILogger Logger { get; set; }
+
+ public DbBackup()
+ { }
+}
+
+var container = new StashboxContainer(options => options
+ .WithAdditionalDependencyAttribute());
+
+container.Register("Console");
+container.Register("File");
+
+container.Register();
+
+// the container will resolve DbBackup with ConsoleLogger.
+IJob job = container.Resolve();
+```
+
+
+
+
+```cs
+class DbBackup : IJob
+{
+ public string Name { get; set; }
+
+ public DbBackup([CustomName] string name)
+ { }
+}
+
+var container = new StashboxContainer(options => options
+ .WithAdditionalDependencyNameAttribute());
+
+container.Register("Backup");
+
+// job.Name is "Backup".
+IJob job = container.Resolve();
+```
+
+
+
+
+
+
+
## Dependency binding
-The same dependency configuration as attributes is available on the registration configuration API.
+The same dependency configuration functionality as attributes, but without attributes.
-- **Bind to parameter**: it has the same functionality as the [Dependency attribute](/docs/guides/service-resolution#attributes) on a constructor or method parameter, enabling the [named resolution](/docs/getting-started/glossary#named-resolution).
+- **Binding to a parameter**: the same functionality as the [`Dependency`](/docs/guides/service-resolution#attributes) attribute on a constructor or method parameter, enabling [named resolution](/docs/getting-started/glossary#named-resolution).
-- **Bind to property/field**: it has the same functionality as the [Dependency attribute](/docs/guides/service-resolution#attributes), enabling the injection of the given property/field.
+- **Binding to a property/field**: the same functionality as the [`Dependency`](/docs/guides/service-resolution#attributes) attribute, enabling the injection of the given property/field.
:::info
There are further dependency binding options [available](/docs/configuration/registration-configuration#dependency-configuration) on the registration configuration API.
diff --git a/docs/src/pages/index.js b/docs/src/pages/index.js
index 72836f0f..d8d6deb1 100644
--- a/docs/src/pages/index.js
+++ b/docs/src/pages/index.js
@@ -24,7 +24,7 @@ function HomepageHeader() {
-
{'$'} dotnet add package Stashbox --version 5.12.2
+
{'$'} dotnet add package Stashbox --version 5.13.0
diff --git a/test/LifetimeTests.cs b/test/LifetimeTests.cs
index ec6bdae3..1b9f52c4 100644
--- a/test/LifetimeTests.cs
+++ b/test/LifetimeTests.cs
@@ -547,6 +547,17 @@ public void LifetimeTests_AutoLifetime_Remains_Transient(CompilerType compilerTy
Assert.NotSame(container.Resolve
(), container.Resolve());
}
+
+ [Theory]
+ [ClassData(typeof(CompilerTypeTestData))]
+ public void LifetimeTests_AutoLifetime_Without_Dependency(CompilerType compilerType)
+ {
+ using IStashboxContainer container = new StashboxContainer(c => c.WithCompiler(compilerType));
+
+ container.Register(c => c.WithAutoLifetime(Lifetimes.Singleton));
+
+ Assert.NotSame(container.Resolve(), container.Resolve());
+ }
interface ITest1 { string Name { get; set; } }