Skip to main content

Registration configuration

Most of the registration methods have an Action<TOptions> parameter, enabling several customization options on the given registration.

Here are three examples that show how the API's usage looks like. They cover the exact functionalities you've read about in the basics section but are achieved with the options API.

This is how you can use the options API to set a registration's name:

container.Register<IJob, DbBackup>(options => options
.WithName("DbBackup"));

The registration configuration API is fluent, which means all option methods can be chained after each other. This provides an easier way to configure complicated registrations.

container.Register<IJob, DbBackup>(options => options
.WithName("DbBackup")
.WithLifetime(Lifetimes.Singleton)
.WithoutDisposalTracking());

General options

WithName

Sets the name identifier of the registration.

container.Register<ILogger, ConsoleLogger>(config => config
.WithName("Console"));

WithInstance

Sets an existing instance for the registration.

Passing true for the wireUp parameter means that the container performs member / method injection on the registered instance.

container.Register<ILogger>(options => options
.WithInstance(new ConsoleLogger()));

WithoutDisposalTracking

Force disables the disposal tracking on the registration.

container.Register<ILogger, ConsoleLogger>(options => options
.WithoutDisposalTracking());

WithMetadata

Sets additional metadata for the registration. It's attached to the service upon its resolution through ValueTuple<,>, Tuple<,>, or Metadata<,> wrappers.

container.Register<IJob, DbBackup>(options => options
.WithMetadata(connectionString));

var jobWithConnectionString = container.Resolve<ValueTuple<IJob, string>>();
Console.WriteLine(jobWithConnectionString.Item2); // prints the connection string.

WithDynamicResolution

Indicates that the service's resolution should be handled by a dynamic Resolve() call on the current IDependencyResolver instead of a pre-built instantiation expression.

container.Register<IJob, DbBackup>();
container.Register<ILogger, ConsoleLogger>(options => options
.WithDynamicResolution());

// new DbBackup(currentScope.Resolve<ILogger>());
var job = container.Resolve<IJob>();

HasServiceType

Used to build conditions based on service type in batch/assembly registrations. It determines whether the registration is mapped to the given service type.

container.RegisterAssemblyContaining<IService1>(configurator: options =>
{
if (options.HasServiceType<IService2>())
options.WithScopedLifetime();
});

Initializer / finalizer

WithFinalizer

Sets a custom cleanup delegate that will be invoked when the scope / container holding the instance is being disposed.

container.Register<ILogger, FileLogger>(options => options
.WithFinalizer(logger => logger
.CloseFile()));

WithInitializer

Sets a custom initializer delegate that will be invoked when the given service is being instantiated.

container.Register<ILogger, FileLogger>(options => options
.WithInitializer((logger, resolver) => logger
.OpenFile()));

Replace

Indicates whether the container should replace an existing registration with the current one (based on implementation type and name). If there's no existing registration in place, the actual one will be added to the registration list.

container.Register<ILogger, ConsoleLogger>(options => options
.ReplaceExisting());

Multiple services

You can read more about binding a registration to multiple services here.

AsImplementedTypes

The service will be mapped to all of its implemented interfaces and base types.

container.Register<IUserRepository, UserRepository>(options => options
.AsImplementedTypes());

AsServiceAlso

Binds the currently configured registration to an additional service type. The registered type must implement or extend the additional service type.

container.Register<IUserRepository, UserRepository>(options => options
.AsServiceAlso<IRepository>()
// or
.AsServiceAlso(typeof(IRepository)));

Dependency configuration

These options allows the same configuration functionality as the dependency attribute.

Binds a constructor / method parameter or a property / field to a named registration by the parameter's type. The container will perform a named resolution on the bound dependency. The second parameter used to set the name of the dependency.

container.Register<IUserRepository, UserRepository>(options => options
.WithDependencyBinding(typeof(ILogger), "FileLogger"));

Lifetime

You can read more about lifetimes here.

WithSingletonLifetime

Sets a singleton lifetime for the registration.

container.Register<ILogger, ConsoleLogger>(config => config
.WithSingletonLifetime());

WithScopedLifetime

Sets a scoped lifetime for the registration.

container.Register<ILogger, ConsoleLogger>(config => config
.WithScopedLifetime());

WithPerScopedRequestLifetime

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.

container.Register<ILogger, ConsoleLogger>(options => options
.WithPerScopedRequestLifetime());

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.

container.Register<ILogger, ConsoleLogger>(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.

container.Register<ILogger, ConsoleLogger>(options => options
.WithAutoLifetime(Lifetimes.Scoped /* boundary lifetime */));

WithLifetime

Sets a custom lifetime for the registration.

container.Register<ILogger, ConsoleLogger>(config => config
.WithLifetime(new CustomLifetime()));

Conditions

You can read more about the concept of conditional resolution here.

WhenHas

Sets an attribute condition for the registration.

container.Register<ILogger, ConsoleLogger>(config => config
.WhenHas<ConsoleAttribute>());

WhenResolutionPathHas

Sets a resolution path condition for the registration. The service will be selected only in the resolution path of the target that has the given attribute. This means that only the direct and sub-dependencies of the target type that has the given attribute will get the configured service.

container.Register<ILogger, ConsoleLogger>(config => config
// Each direct and sub-dependency of any service that has
// a ConsoleAttribute will get FileLogger wherever they
// depend on ILogger.
.WhenResolutionPathHas<ConsoleAttribute>());

WhenDependantIs

Sets a parent target condition for the registration.

container.Register<ILogger, FileLogger>(config => config
.WhenDependantIs<UserRepository>());

WhenInResolutionPathOf

Sets a resolution path condition for the registration. The service will be selected only in the resolution path of the given target. This means that only the direct and sub-dependencies of the target type will get the configured service.

container.Register<ILogger, FileLogger>(config => config
// Each direct and sub-dependency of UserRepository
// will get FileLogger wherever they depend on ILogger.
.WhenInResolutionPathOf<UserRepository>());

When

Sets a custom user-defined condition for the registration.

container.Register<ILogger, FileLogger>(config => config
.When(typeInfo => typeInfo.ParentType == typeof(UserRepository)));

Constructor selection

WithConstructorSelectionRule

Sets the constructor selection rule for the registration.

container.Register<ILogger>(options => options
.WithConstructorSelectionRule(...));

PreferMostParameters

Selects the constructor which has the longest parameter list.

options.WithConstructorSelectionRule(
Rules.ConstructorSelection.PreferMostParameters)

PreferLeastParameters

Selects the constructor which has the shortest parameter list.

options.WithConstructorSelectionRule(
Rules.ConstructorSelection.PreferLeastParameters)

Custom

You can set your own custom constructor ordering logic.

options.WithConstructorSelectionRule(
constructors => { /* custom constructor sorting logic */ })

WithConstructorByArgumentTypes

Selects a constructor by its argument types.

container.Register<IUserRepository, UserRepository>(options => options
.WithConstructorByArgumentTypes(typeof(ILogger)));

WithConstructorByArguments

Selects a constructor by its arguments to use during resolution. These arguments are used to invoke the selected constructor.

container.Register<IUserRepository, UserRepository>(options => options
.WithConstructorByArguments(new ConsoleLogger()));

Property / field Injection

WithAutoMemberInjection

Enables the auto member injection and sets the rule for it.

container.Register<IUserRepository, UserRepository>(options => options
.WithAutoMemberInjection(...));

PropertiesWithPublicSetter

With this flag, the container will perform auto-injection on properties with a public setter.

options.WithAutoMemberInjection(
Rules.AutoMemberInjectionRules.PropertiesWithPublicSetter)

PropertiesWithLimitedAccess

With this flag, the container will perform auto-injection on properties which has a non-public setter as well.

options.WithAutoMemberInjection(
Rules.AutoMemberInjectionRules.PropertiesWithLimitedAccess)

PrivateFields

With this flag, the container will perform auto-injection on private fields too.

options.WithAutoMemberInjection(
Rules.AutoMemberInjectionRules.PrivateFields)

Combined rules

As these rules are bit flags, you can use them combined together with bitwise logical operators.

options.WithAutoMemberInjection(Rules.AutoMemberInjectionRules.PrivateFields | 
Rules.AutoMemberInjectionRules.PropertiesWithPublicSetter)

Member selection filter

You can pass your own member selection logic to control which members should be auto injected.

container.Register<ILogger, ConsoleLogger>(options => options
.WithAutoMemberInjection(filter: member => member.Type != typeof(ILogger)));

Required member injection

With this option, you can enable or disable the auto injection of members defined with C# 11's required keyword.

container.Register<IUserRepository, UserRepository>(options => options
.WithRequiredMemberInjection(enabled: false));
note

The required member injection option is enabled by default.

Injection parameters

WithInjectionParameters

Sets multiple injection parameters for the registration.

container.Register<IUserRepository, UserRepository>(options => options
.WithInjectionParameters(new KeyValuePair<string, object>("logger", new ConsoleLogger()));

WithInjectionParameter

Sets a single injection parameter for the registration.

container.Register<IUserRepository, UserRepository>(options => options
.WithInjectionParameter("logger", new ConsoleLogger());

Factory

You can read more about the concept of factory registration here.

WithFactory - Sets a factory delegate that could take various number of pre-resolved dependencies as parameters and returns the service instance.

// 1 parameter factory
container.Register<IUserRepository, UserRepository>(options => options
.WithFactory<ILogger>(logger => new UserRepository(logger));

// 2 parameters factory
container.Register<IUserRepository, UserRepository>(options => options
.WithFactory<ILogger, IDbContext>((logger, context) => new UserRepository(logger, context));

// 3 parameters factory
container.Register<IUserRepository, UserRepository>(options => options
.WithFactory<ILogger, IDbContext, IOptions>((logger, context, options) =>
new UserRepository(logger, context, options));

// 4 parameters factory
container.Register<IUserRepository, UserRepository>(options => options
.WithFactory<ILogger, IDbConnection, IOptions, IUserValidator>((logger, connection, options, validator) =>
new UserRepository(logger, connection, options, validator));

// 5 parameters factory
container.Register<IUserRepository, UserRepository>(options => options
.WithFactory<ILogger, IDbConnection, IOptions, IUserValidator, IPermissionManage>(
(logger, connection, options, validator, permissionManager) =>
new UserRepository(logger, connection, options, validator, permissionManager));

You can also get the current dependency resolver as a pre-resolved parameter:

container.Register<IUserRepository, UserRepository>(options => options
.WithFactory<ILogger, IDependencyResolver>((logger, resolver) =>
new UserRepository(logger, resolver.Resolve<IDbConnection>())));
info

All factory configuration method has an isCompiledLambda parameter which should be set to true if the passed delegate is compiled from an Expression tree.

Scope definition

You can read more about the concept of defined scopes here.

InNamedScope

Sets a scope name condition for the registration; it will be used only when a scope with the same name requests it.

container.Register<IUserRepository, UserRepository>(options => options
.InNamedScope("UserRepo"));

InScopeDefinedBy

Sets a condition for the registration; it will be used only within the scope defined by the given type.

container.Register<ILogger, ConsoleLogger>(options => options
.InScopeDefinedBy<UserRepository>());
container.Register<IUserRepository, UserRepository>(options => options
.DefinesScope());

DefinesScope

This registration is used as a logical scope for it's dependencies. Dependencies registered with InNamedScope() with the same name are preferred during resolution. When the name is not set, the service type is used as the name. Dependencies registered with InScopeDefinedBy() are selected.

container.Register<IUserRepository, UserRepository>(options => options
.DefinesScope("UserRepo"));

// or
container.Register<IUserRepository, UserRepository>(options => options
.DefinesScope());

Decorator specific

You can read more about decorators here.

WhenDecoratedServiceIs

Sets a decorated target condition for the registration.

container.RegisterDecorator<ILogger, LoggerDecorator>(options => options
.WhenDecoratedServiceIs<FileLogger>());

WhenDecoratedServiceHas

Sets an attribute condition that the decorated target has to satisfy.

container.RegisterDecorator<ILogger, LoggerDecorator>(options => options
.WhenDecoratedServiceHas<DetailedLoggingAttribute>());

Unknown registration specific

You can read more about unknown type resolution here.

SetImplementationType

Sets the current registration's implementation type.

var container = new StashboxContainer(c => c.WithUnknownTypeResolution(config =>
{
if (config.ServiceType == typeof(IService))
config.SetImplementationType(typeof(Service));
}));

Skip

Marks the current unknown type registration as skipped.

var container = new StashboxContainer(c => c.WithUnknownTypeResolution(config =>
{
if (config.ServiceType == typeof(IService))
config.Skip();
}));