You can bind a factory delegate to a registration that the container will invoke directly to instantiate your service.
You can use parameter-less and custom parameterized delegates as a factory. [Here](/docs/configuration/registration-configuration#factory) is the list of all available options.
You can also get the current [dependency resolver](/docs/getting-started/glossary#dependency-resolver) as a delegate parameter to resolve any additional dependencies required for the service construction.
```cs
container.Register(options => options
.WithFactory(() => new ConsoleLogger());
// the container uses the factory for instantiation.
IJob job = container.Resolve();
```
```cs
container.Register(options => options
.WithFactory(logger => new DbBackup(logger));
// the container uses the factory for instantiation.
IJob job = container.Resolve();
```
```cs
container.Register(options => options
.WithFactory(resolver => new DbBackup(resolver.Resolve()));
// the container uses the factory for instantiation.
IJob job = container.Resolve();
```
Delegate factories are useful when your service's instantiation is not straight-forward for the container, like when it depends on something that is not available at resolution time. E.g., a connection string.
```cs
container.Register(options => options
.WithFactory(logger =>
new DbBackup(Configuration["DbConnectionString"], logger));
```
### Factories with parameter overrides
Stashbox can implicitly [wrap](/docs/advanced/wrappers-resolvers#delegate) your service in a `Delegate` and lets you pass parameters that can override your service's dependencies. Moreover, you can register your own custom delegate that the container will resolve when you request your service wrapped in a `Delegate`.
```cs
container.RegisterFunc((connectionString, resolver) =>
new DbBackup(connectionString, resolver.Resolve()));
Func backupFactory = container.Resolve>();
IJob dbBackup = backupFactory(Configuration["ConnectionString"]);
```
```cs
container.RegisterFunc((connectionString, resolver) =>
new DbBackup(connectionString, resolver.Resolve()));
Delegate backupFactory = container.ResolveFactory(typeof(IJob),
parameterTypes: new[] { typeof(string) });
IJob dbBackup = backupFactory.DynamicInvoke(Configuration["ConnectionString"]);
```
If a service has multiple constructors, the container visits those first, that has matching parameters passed to the factory, with respecting the additional [constructor selection rules](/docs/configuration/registration-configuration#constructor-selection).
```cs
class Service
{
public Service(int number) { }
public Service(string text) { }
}
container.Register();
// create the factory with an int input parameter.
var func = constainer.Resolve>();
// the constructor with the int param
// is used for instantiation.
var service = func(2);
```
### Consider this before using the resolver parameter inside a factory
Delegate factories are a black-box for the container. It doesn't have control over what's happening inside a delegate, which means when you resolve additional dependencies with the [dependency resolver](/docs/getting-started/glossary#dependency-resolver) parameter, they could easily bypass the [lifetime](/docs/diagnostics/validation#lifetime-validation) and [circular dependency](/docs/diagnostics/validation#circular-dependency) validations. Fortunately, you have the option to keep them validated anyway with parameterized factory delegates.
#### Delegates with dependencies passed as parameters
Rather than using the [dependency resolver](/docs/getting-started/glossary#dependency-resolver) parameter inside the factory, let the container inject the dependencies into the delegate as parameters. This way, the [resolution tree's](/docs/getting-started/glossary#resolution-tree) integrity remains stable because no service resolution happens inside the black-box, and each parameter is validated.
```cs
interface IEventProcessor { }
class EventProcessor : IEventProcessor
{
public EventProcessor(ILogger logger, IEventValidator validator)
{ }
}
container.Register();
container.Register();
container.Register(options => options
// Ilogger and IEventValidator instances are injected
// by the container at resolution time, so they will be
// validated against circular and captive dependencies.
.WithFactory((logger, validator) =>
new EventProcessor(logger, validator));
// the container resolves ILogger and IEventValidator first, then
// it passes them to the factory as delegate parameters.
IEventProcessor processor = container.Resolve();
```
### Accessing the currently resolving type in factories
To access the currently resolving type in factory delegates, you can set the `TypeInformation` type as an input parameter of the factory.
The `TypeInformation` holds every reflected context information about the currently resolving type.
This can be useful when the resolution is, e.g., in an open generic context, and we want to know which closed generic variant is requested.
```cs
interface IService { }
class Service : IService { }
container.Register(typeof(IService<>), typeof(Service<>), options =>
options.WithFactory(typeInfo =>
{
// typeInfo.Type here holds the actual type like
// IService based on the resolution request below.
}));
container.Resolve>();
```
## Multiple implementations
As we previously saw in the [Named registration](/docs/guides/basics#named-registration) topic, Stashbox allows you to have multiple implementations bound to a particular [service type](/docs/getting-started/glossary#service-type--implementation-type). You can use names to distinguish them, but you can also access them by requesting a typed collection using the [service type](/docs/getting-started/glossary#service-type--implementation-type).
:::note
The returned collection is in the same order as the services were registered.
Also, to request a collection, you can use any interface implemented by an array.
:::
```cs
container.Register();
container.Register();
container.Register();
```
```cs
// jobs contain all three services in registration order.
IEnumerable jobs = container.ResolveAll();
```
```cs
// jobs contain all three services in registration order.
IJob[] jobs = container.Resolve();
```
```cs
// jobs contain all three services in registration order.
IEnumerable jobs = container.Resolve>();
```
```cs
// jobs contain all three services in registration order.
IList jobs = container.Resolve>();
```
```cs
// jobs contain all three services in registration order.
ICollection jobs = container.Resolve>();
```
When you have multiple implementations registered to a service, a request to the [service type](/docs/getting-started/glossary#service-type--implementation-type) without a name will return the **last registered implementation**.
:::info
Not only names can be used to distinguish registrations, [conditions](/docs/guides/service-resolution#conditional-resolution), [named scopes](/docs/guides/scopes#named-scopes), and [metadata](/docs/advanced/wrappers-resolvers#metadata--tuple) can also influence the results.
:::
```cs
container.Register();
container.Register();
container.Register();
// job will be the ImageProcess.
IJob job = container.Resolve();
```
## Binding to multiple services
When you have a service that implements multiple interfaces, you have the option to bind its registration to all or some of those additional interfaces or base types.
Suppose we have the following class declaration:
```cs
class DbBackup : IJob, IScheduledJob
{
public DbBackup() { }
}
```
```cs
container.Register(options => options
.AsServiceAlso());
IJob job = container.Resolve(); // DbBackup
IScheduledJob job = container.Resolve(); // DbBackup
DbBackup job = container.Resolve(); // error, not found
```
```cs
container.Register(options => options
.AsImplementedTypes());
IJob job = container.Resolve(); // DbBackup
IScheduledJob job = container.Resolve(); // DbBackup
DbBackup job = container.Resolve(); // DbBackup
```
## Batch registration
You have the option to register multiple services in a single registration operation.
**Filters (optional):**
First, the container will use the *implementation filter* action to select only those types from the collection we want to register. When we have those, the container will execute the *service filter* on their implemented interfaces and base classes to select which [service type](/docs/getting-started/glossary#service-type--implementation-type) they should be mapped to.
:::note
Framework types like `IDisposable` are excluded from being considered as a [service type](/docs/getting-started/glossary#service-type--implementation-type) by default.
:::
:::tip
You can use the registration configuration API to configure individual registrations.
:::
This example will register three types to all their implemented interfaces, extended base classes, and to themselves ([self registration](/docs/getting-started/glossary#self-registration)) without any filter:
```cs
container.RegisterTypes(new[]
{
typeof(DbBackup),
typeof(ConsoleLogger),
typeof(StorageCleanup)
});
IEnumerable jobs = container.ResolveAll(); // 2 items
ILogger logger = container.Resolve(); // ConsoleLogger
IJob job = container.Resolve(); // StorageCleanup
DbBackup backup = container.Resolve(); // DbBackup
```
In this example, we assume that `DbBackup` and `StorageCleanup` are implementing `IDisposable` besides `IJob` and also extending a `JobBase` abstract class.
```cs
container.RegisterTypes(new[]
{ typeof(DbBackup), typeof(ConsoleLogger), typeof(StorageCleanup) },
// implementation filter, only those implementations that implements IDisposable
impl => typeof(IDisposable).IsAssignableFrom(impl),
// service filter, register them to base classes only
(impl, service) => service.IsAbstract && !service.IsInterface);
IEnumerable jobs = container.ResolveAll(); // 0 items
IEnumerable jobs = container.ResolveAll(); // 2 items
ILogger logger = container.Resolve(); // error, not found
DbBackup backup = container.Resolve(); // DbBackup
```
This example ignores the [self registrations](/docs/getting-started/glossary#self-registration) completely:
```cs
container.RegisterTypes(new[]
{
typeof(DbBackup),
typeof(ConsoleLogger),
typeof(StorageCleanup)
},
registerSelf: false);
IEnumerable jobs = container.ResolveAll(); // 2 items
ILogger logger = container.Resolve(); // ConsoleLogger
DbBackup backup = container.Resolve(); // error, not found
ConsoleLogger logger = container.Resolve(); // error, not found
```
This example will configure all registrations mapped to `ILogger` as `Singleton`:
```cs
container.RegisterTypes(new[]
{
typeof(DbBackup),
typeof(ConsoleLogger),
typeof(StorageCleanup)
},
configurator: options =>
{
if (options.HasServiceType())
options.WithSingletonLifetime();
});
ILogger logger = container.Resolve(); // ConsoleLogger
ILogger newLogger = container.Resolve(); // the same ConsoleLogger
IEnumerable jobs = container.ResolveAll(); // 2 items
```
Another type of service filter is the `.RegisterTypesAs()` method, which registers only those types that implements the `T` [service type](/docs/getting-started/glossary#service-type--implementation-type).
:::note
This method also accepts an implementation filter and a registration configurator action like `.RegisterTypes()`.
:::
:::caution
`.RegisterTypesAs()` doesn't create [self registrations](/docs/getting-started/glossary#self-registration) as it only maps the implementations to the given `T` [service type](/docs/getting-started/glossary#service-type--implementation-type).
:::
```cs
container.RegisterTypesAs(new[]
{
typeof(DbBackup),
typeof(ConsoleLogger),
typeof(StorageCleanup)
});
IEnumerable jobs = container.ResolveAll(); // 2 items
ILogger logger = container.Resolve(); // error, not found
IJob job = container.Resolve(); // StorageCleanup
DbBackup backup = container.Resolve(); // error, not found
```
```cs
container.RegisterTypesAs(typeof(IJob), new[]
{
typeof(DbBackup),
typeof(ConsoleLogger),
typeof(StorageCleanup)
});
IEnumerable jobs = container.ResolveAll(); // 2 items
ILogger logger = container.Resolve(); // error, not found
IJob job = container.Resolve(); // StorageCleanup
DbBackup backup = container.Resolve(); // error, not found
```
## Assembly registration
The batch registration API *(filters, registration configuration action, self-registration)* is also usable for registering services from given assemblies.
In this example, we assume that the same three services we used in the [batch registration](#batch-registration) section are in the same assembly.
:::info
The container also detects and registers open-generic definitions (when applicable) from the supplied type collection. You can read about [open-generics here](/docs/advanced/generics#open-generics).
:::
```cs
container.RegisterAssembly(typeof(DbBackup).Assembly,
// service filter, register to interfaces only
serviceTypeSelector: (impl, service) => service.IsInterface,
registerSelf: false,
configurator: options => options.WithoutDisposalTracking()
);
IEnumerable jobs = container.ResolveAll(); // 2 items
IEnumerable jobs = container.ResolveAll(); // 0 items
ILogger logger = container.Resolve(); // ConsoleLogger
DbBackup backup = container.Resolve(); // error, not found
```
```cs
container.RegisterAssemblies(new[]
{
typeof(DbBackup).Assembly,
typeof(JobFromAnotherAssembly).Assembly
},
// service filter, register to interfaces only
serviceTypeSelector: (impl, service) => service.IsInterface,
registerSelf: false,
configurator: options => options.WithoutDisposalTracking()
);
IEnumerable jobs = container.ResolveAll(); // 2 items
IEnumerable jobs = container.ResolveAll(); // 0 items
ILogger logger = container.Resolve(); // ConsoleLogger
DbBackup backup = container.Resolve(); // error, not found
```
```cs
container.RegisterAssemblyContaining(
// service filter, register to interfaces only
serviceTypeSelector: (impl, service) => service.IsInterface,
registerSelf: false,
configurator: options => options.WithoutDisposalTracking()
);
IEnumerable jobs = container.ResolveAll(); // 2 items
IEnumerable jobs = container.ResolveAll(); // 0 items
ILogger logger = container.Resolve(); // ConsoleLogger
DbBackup backup = container.Resolve(); // error, not found
```
## Composition root
The [Composition Root](https://blog.ploeh.dk/2011/07/28/CompositionRoot/) is an entry point where all services required to make a component functional are wired together.
Stashbox provides an `ICompositionRoot` interface that can be used to define an entry point for a given component or even for an entire assembly.
You can wire up your *composition root* implementation with `ComposeBy()`, or you can let the container find and execute all available *composition root* implementations within an assembly.
:::note
Your `ICompositionRoot` implementation also can have dependencies that the container will resolve.
:::
```cs
class ExampleRoot : ICompositionRoot
{
public ExampleRoot(IDependency rootDependency)
{ }
public void Compose(IStashboxContainer container)
{
container.Register();
container.Register();
}
}
```
```cs
// compose a single root.
container.ComposeBy();
```
```cs
// compose every root in the given assembly.
container.ComposeAssembly(typeof(IServiceA).Assembly);
```
```cs
// compose a single root with dependency override.
container.ComposeBy(new CustomRootDependency());
```
## Injection parameters
If you have pre-evaluated dependencies you'd like to inject at resolution time, you can set them as injection parameters during registration.
:::note
Injection parameter names are matched to constructor arguments or field/property names.
:::
```cs
container.Register(options => options
.WithInjectionParameter("logger", new ConsoleLogger())
.WithInjectionParameter("eventBroadcaster", new MessageBus());
// the injection parameters will be passed to DbBackup's constructor.
IJob backup = container.Resolve();
```
## Initializer / finalizer
The container provides specific extension points to let you react to lifetime events of an instantiated service.
For this reason, you can specify *Initializer* and *Finalizer* delegates. The *finalizer* is called upon the service's [disposal](/docs/guides/scopes#disposal), and the *initializer* is called upon the service's construction.
```cs
container.Register(options => options
// delegate that called right after instantiation.
.WithInitializer((logger, resolver) => logger.OpenFile())
// delegate that called right before the instance's disposal.
.WithFinalizer(logger => logger.CloseFile()));
```
================================================
FILE: docs/docs/guides/basics.md
================================================
import CodeDescPanel from '@site/src/components/CodeDescPanel';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# Basic usage
This section is about the basics of Stashbox's API. It will give you a good starting point for more advanced topics described in the following sections.
Stashbox provides several methods that enable registering services, and we'll go through the most common scenarios with code examples.
## Default registration
Stashbox allows registration operations via the `Register()` methods.
During registration, the container checks whether the [service type](/docs/getting-started/glossary#service-type--implementation-type) is assignable from the [implementation type](/docs/getting-started/glossary#service-type--implementation-type) and if not, the container throws an [exception](/docs/diagnostics/validation#registration-validation).
Also, when the implementation is not resolvable, the container throws the same [exception](/docs/diagnostics/validation#registration-validation).
The example registers `DbBackup` to be returned when `IJob` is requested.
```cs
container.Register();
IJob job = container.Resolve();
// throws an exception because ConsoleLogger doesn't implement IJob.
container.Register();
// throws an exception because IJob is not a valid implementation.
container.Register();
```
```cs
container.Register(typeof(IJob), typeof(DbBackup));
object job = container.Resolve(typeof(IJob));
// throws an exception because ConsoleLogger doesn't implement IJob.
container.Register(typeof(IJob), typeof(ConsoleLogger));
// throws an exception because IJob is not a valid implementation.
container.Register(typeof(IJob), typeof(IJob));
```
You can register a service to itself without specifying a [service type](/docs/getting-started/glossary#service-type--implementation-type), only the implementation ([self registration](/docs/getting-started/glossary#self-registration)).
In this case, the given implementation is considered the [service type](/docs/getting-started/glossary#service-type--implementation-type) and must be used to request the service (`DbBackup` in the example).
```cs
container.Register();
DbBackup backup = container.Resolve();
```
```cs
container.Register(typeof(DbBackup));
object backup = container.Resolve(typeof(DbBackup));
```
The example shows how you can bind more implementations to a [service type](/docs/getting-started/glossary#service-type--implementation-type) using names for identification.
The same name must be used to resolve the named service.
:::note
The name is an `object` type.
:::
```cs
container.Register("DbBackup");
container.Register("StorageCleanup");
IJob cleanup = container.Resolve("StorageCleanup");
```
```cs
container.Register(typeof(IJob), typeof(DbBackup), "DbBackup");
container.Register(typeof(IJob), typeof(StorageCleanup), "StorageCleanup");
object cleanup = container.Resolve(typeof(IJob), "StorageCleanup");
```
With instance registration, you can provide an already created external instance to use when the given [service type](/docs/getting-started/glossary#service-type--implementation-type) is requested.
Stashbox automatically handles the [disposal](/docs/guides/scopes#disposal) of the registered instances, but you can turn this feature off with the `withoutDisposalTracking` parameter.
When an `IJob` is requested, the container will always return the external instance.
```cs
var job = new DbBackup();
container.RegisterInstance(job);
// resolvedJob and job are the same.
IJob resolvedJob = container.Resolve();
```
```cs
var job = new DbBackup();
container.RegisterInstance(job, typeof(IJob));
// resolvedJob and job are the same.
object resolvedJob = container.Resolve(typeof(IJob));
```
```cs
var job = new DbBackup();
container.RegisterInstance(job, "DbBackup");
// resolvedJob and job are the same.
IJob resolvedJob = container.Resolve("DbBackup");
```
```cs
var job = new DbBackup();
container.RegisterInstance(job, withoutDisposalTracking: true);
// resolvedJob and job are the same.
IJob resolvedJob = container.Resolve();
```
With re-map, you can bind new implementations to a [service type](/docs/getting-started/glossary#service-type--implementation-type) and delete old registrations in one action.
:::caution
When there are multiple registrations mapped to a [service type](/docs/getting-started/glossary#service-type--implementation-type), `.ReMap()` will replace all of them with the given [implementation type](/docs/getting-started/glossary#service-type--implementation-type). If you want to replace only one specific service, use the `.ReplaceExisting()` [configuration option](/docs/configuration/registration-configuration#replace).
:::
```cs
container.Register();
container.Register();
// jobs contain all two jobs
IEnumerable jobs = container.ResolveAll();
container.ReMap();
// jobs contains only the SlackMessageSender
jobs = container.ResolveAll();
```
```cs
container.Register(typeof(IJob), typeof(DbBackup));
container.Register(typeof(IJob), typeof(StorageCleanup));
// jobs contain all two jobs
IEnumerable
## Wiring up
A service's lifetime indicates how long its instance will live and which re-using policy should be applied when it gets injected.
This example shows how you can use the registration API's shortcuts for lifetimes. These are just sugars, and there are more ways explained in the [lifetimes](/docs/guides/lifetimes) section.
:::info
The `DefaultLifetime` is [configurable](/docs/guides/lifetimes#default-lifetime).
:::
When no lifetime is specified, the service will use the container's `DefaultLifetime`, which is `Transient` by default.
```cs
container.Register();
IJob job = container.Resolve();
```
A service with `Singleton` lifetime will be instantiated once and reused during the container's lifetime.
```cs
container.RegisterSingleton();
IJob job = container.Resolve();
```
The `Scoped` lifetime behaves like a `Singleton` within a [scope](/docs/guides/scopes).
A scoped service is instantiated once and reused during the scope's whole lifetime.
```cs
container.RegisterScoped();
IJob job = container.Resolve();
```
================================================
FILE: docs/docs/guides/lifetimes.md
================================================
import CodeDescPanel from '@site/src/components/CodeDescPanel';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# Lifetimes
Lifetime management controls how long a service's instances will live (from instantiation to [disposal](/docs/guides/scopes#disposal)) and how they will be reused between resolution requests.
:::info
Choosing the right lifetime helps you avoid [captive dependencies](/docs/diagnostics/validation#lifetime-validation).
:::
## Default lifetime
When you are not specifying a lifetime during registration, Stashbox will use the default lifetime. By default, it's set to [Transient](#transient-lifetime), but you can override it with the `.WithDefaultLifetime()` [container configuration option](/docs/configuration/container-configuration#default-lifetime).
You can choose either from the pre-defined lifetimes defined on the `Lifetimes` static class or use a [custom lifetime](#custom-lifetime).
```cs
var container = new StashboxContainer(options => options
.WithDefaultLifetime(Lifetimes.Transient));
```
```cs
var container = new StashboxContainer(options => options
.WithDefaultLifetime(Lifetimes.Singleton));
```
```cs
var container = new StashboxContainer(options => options
.WithDefaultLifetime(Lifetimes.Scoped));
```
## Transient lifetime
A new instance is created for each [scope](/docs/guides/scopes), which will be returned for every resolution request initiated on the given scope. It's like a singleton lifetime within a scope.
:::note
Scoped services are disposed when their scope is being disposed.
:::
```cs
container.Register(options => options
.WithLifetime(Lifetimes.Scoped));
using var scope = container.BeginScope();
IJob job = scope.Resolve();
```
```cs
container.RegisterScoped();
using var scope = container.BeginScope();
IJob job = scope.Resolve();
```
## Named scope lifetime
It is the same as scoped lifetime, except the given service will be selected only when a scope with the same name initiates the resolution request.
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.
```cs
container.Register(options => options
.InNamedScope("DbScope"));
using var scope = container.BeginScope("DbScope");
IJob job = scope.Resolve();
```
```cs
container.Register(options => options
.DefinesScope());
ontainer.Register(options => options
.InScopeDefinedBy());
// the executor will begin a new scope within itself
// when it gets resolved and DbBackup will be selected
// and attached to that scope instead.
using var scope = container.BeginScope();
DbJobExecutor executor = scope.Resolve();
```
```cs
container.Register(options => options
.DefinesScope("DbScope"));
ontainer.Register(options => options
.InNamedScope("DbScope"));
// the executor will begin a new scope within itself
// when it gets resolved and DbBackup will be selected
// and attached to that scope instead.
using var scope = container.BeginScope();
DbJobExecutor executor = scope.Resolve();
```
:::note
Services with named scope lifetime are disposed when the related named scope is being disposed.
:::
## Per-request lifetime