Skip to content

Service registration

Peter Csajtai edited this page Feb 27, 2019 · 16 revisions

Standard

The following example shows how you can bind your service to an interface or abstract base class.

container.Register<ICreature, Elf>();
//or without generic parameters
container.Register(typeof(ICreature), typeof(Elf));

//resolution
var creature = container.Resolve<ICreature>();
//or without generic parameters
var creature = container.Resolve(typeof(ICreature));

The container will resolve the Elf type as an ICreature.

You can also register a service to itself, without specifying a base.

container.Register<Elf>();
//or without generic parameters
container.Register(typeof(Elf));

//resolution
var elf = container.Resolve<Elf>();
//or without generic parameters
var elf = container.Resolve(typeof(Elf));

Named

If you want to bind more implementations to a single service type then you can name your registration in the following way:

container.Register<ICreature, Dwarf>("Bruenor");
container.Register<ICreature, Drow>("Drizzt");

//resolution
var creature = container.Resolve<ICreature>("Bruenor");

As the name is an object it can be anything.

Instance

In some cases, you may want to use already instantiated services as dependencies:

var elf = new Elf();
container.RegisterInstanceAs<ICreature>(elf);

The container will always return with the prepared instance, when an ICreature is requested.

ReMap

Stashbox supports the remapping of already registered services:

container.Register<IDwarf, Bruenor>();
container.ReMap<IDwarf, Pwent>();

If there are multiple services mapped to an interface, ReMap will replace all of them with the given one, if you want to replace only one specified service, use the ReplaceExisting() configuration e.g. container.Register<IDwarf, Bruenor>(context => context.ReplaceExisting());

WireUp

Similar to the Instance registration except that this type of registration will tell the container, that it should execute further operations (member injection, container extensions, etc.) on the registered instance.

var elf = new Elf();
container.WireUp<ICreature>(elf);

BuildUp

Similar to the WireUp except that the given instance will not be registered into the container.

var elf = new Elf();
var builtElf = container.BuildUp<ICreature>(elf);

Injection parameters

If you have some special parameters in your services constructor which you'd like to set manually (primitive types for example, or some pre-evaluated values) you can use injection parameters.

class Drizzt : IDrow
{
    public Drizzt(IWeapon leftHand, IWeapon rightHand)
    {
        //...
    }
}
container.Register<IWeapon, Twinkle>();
container.Register<IDrow, Drizzt>(context => context.WithInjectionParameters(new InjectionParameter { Value = new Icingdeath(), Name = "rightHand" });

The configuration above indicates that Stashbox will inject the pre-evaluated Icingdeath object as Drizzts right-hand weapon and the other one will be resolved through the standard registration.

Resolver factory

You can also register a service with a factory delegate which gets the current lifetime scope as an argument.

container.Register<IDrow, Drizzt>(context => context.WithFactory(resolver => new Drizzt(resolver.Resolve<IWeapon>("Twinkle"), resolver.Resolve<IWeapon>("Icingdeath")));

container.Resolve<IDrow>(); //the container will use the given factory to instantiate Drizzt.

Factory registration

You can register a Func<> delegate as a service factory:

container.RegisterFunc<IDrow>(resolver => new Drizzt(resolver.Resolve<IWeapon>()));

Then you can access the registered factory by requesting a Func<> type:

var factory = container.Resolve<Func<IDrow>>();
var drizzt = factory();

Custom parameters

You can also register factory delegates with custom parameters:

container.RegisterFunc<IWeapon, IWeapon, IDrow>((leftHand, rightHand, resolver) => new Drizzt(leftHand, rightHand));

//then request the factory
var factory = container.Resolve<Func<IWeapon, IWeapon, IDrow>>();
var drizzt = factory(new Icingdeath(), new Twinkle());

Multiple registration

You can register a service collection with several options:

class Bruenor : IDwarf, ICreature { /*...*/ }
class Drizzt : IDrow, ICreature { /*...*/ }

container.RegisterTypes(new[] { new Drizzt(), new Bruenor() });

The example above will register the services mapped to their impemented interfaces and also themselves, so the mapping will look like:

  • ICreature -> Drizzt
  • ICreature -> Bruenor
  • IDrow -> Drizzt
  • IDwarf -> Bruenor
  • Drizzt -> Drizzt
  • Bruenor -> Bruenor

Filters

You can specify a filter which can be used to determine which types you want to register:

Single type filter

container.RegisterTypesAs<IDrow>(new[] { new Drizzt(), new Bruenor() });

In this case only Drizzt will be registered into the container.

Filter predicate

container.RegisterTypes<IDrow>(new[] { new Drizzt(), new Bruenor() }, type => type == typeof(Bruenor));

In this case only Bruenor will be registered into the container.

Configuration

You can also pass a lambda expression which can be used to customize the registration of each type:

container.RegisterTypes(new[] { new Drizzt(), new Bruenor() }, configurator: context => 
{
    if(context.ImplementationType == typeof(Drizzt))
        context.WithScopedLifetime();
});

The example above will register Drizzt with scoped lifetime and leaves the other services with the default configuration.

Assembly registration

You can register services defined within a given assembly:

container.RegisterAssembly(assembly);

In this case all those services which are defined in the given assembly will be registered by their implemented interfaces and base types, and also by themselves.

You can also register an assembly by using just a type defined in it:

container.RegisterAssemblyContaining<Drizzt>();

You can pass an assembly collection also:

container.RegisterAssemblies(new[] { assembly1, assembly2 });

Filter

You can specify which services you want to register from an assembly with a filter predicate:

container.RegisterAssembly(assembly, type => type == typeof(Drizzt));
//or
container.RegisterAssemblyContaining<Drizzt>(type => type == typeof(Drizzt));
//or 
container.RegisterAssemblies(new[] { assembly1, assembly2 }, type => type == typeof(Drizzt));

Configuration

You can use the configurator parameter to configure the service registrations:

container.RegisterAssembly(assembly, configurator: context => context.WithScopedLifetime());

Composition

Stashbox supports the service registration via ICompositionRoot implementations.

class DwarfCompositionRoot : ICompositionRoot
{
    public void Compose(IStashboxContainer container)
    {
       container.Register<IDwarf, Bruenor>();
       container.Register<IDwarf, Pwent>();
    }
}

You can wire up your ICompositionRoot implementation by setting it explicitly during the registration:

container.ComposeBy<DwarfCompositionRoot>();
//... other composition root registrations

You can also let the container find and execute the available ICompositionRoot implementations inside an assembly or assemblies:

container.ComposeAssembly(assembly);
//or
container.ComposeAssemblies(new[] { assembly1, assembly2 });

The container will search for ICompositionRoot implementations inside the given assemblies and will execute their Compose() method passing itself as a parameter, so ONLY the services that are registered in them will be wired into the container, any other services inside the assembly/assemblies will be excluded.