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());

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();
}));