Skip to content
This repository has been archived by the owner on Feb 6, 2021. It is now read-only.

Registration

Guy Antoine edited this page Oct 30, 2020 · 11 revisions

Basic Registration

By default, all registrations result in a new instance being created every time the type is resolved. See Lifecycle Management for more on this.

All registrations below result in the same behaviour. Resolving container.Resolve<FrenchPress> will create a new instance of FrenchPress.

//Register FrenchPress as FrenchPress
container.Register<FrenchPress>();

container.RegisterType(typeof(FrenchPress));

//Register an expression that executes to create a FrenchPress
container.RegisterExplicit<FrenchPress, FrenchPress>(c => new FrenchPress());

To expose a type's interface, register as follows. Resolving container.Resolve<IBrewEquipment> will create a new instance of FrenchPress.

//Register a FrenchPress as IBrewEquipment
container.Register<IBrewEquipment, FrenchPress>();

container.RegisterType(typeof(FrenchPress), typeof(IBrewEquipment));

//Register an expression that executes to create a FrenchPress
//as IBrewEquipment
container.RegisterExplicit<IBrewEquipment, FrenchPress>(c => new FrenchPress());

Registering an Instance

There may be times where you want to pass an object instance to the container. This will be resolved as a Singleton.

var glassPlunger = new FrenchPress();

//Register glassPlunger object as FrenchPress
container.RegisterInstance(glassPlunger);

//Register glassPlunger object as IBrewEquipment
container.RegisterInstance<IBrewEquipment>(glassPlunger);

Lifecycle Management

📝 By default all registrations are transient, resulting in a new instance being created every time the type is resolved.

To register as a Single Instance / Singleton, append .SingleInstance().

container
  .Register<FrenchPress>()
  .SingleInstance();

container
  .Register<IBrewEquipment, FrenchPress>()
  .SingleInstance();

Named Registrations

📝 By default only one registration is allowed per container

The following would result in a SmartDi.RegisterException with the message 'FrenchPress is already registered.'

container.Register<FrenchPress>();
container.Register<FrenchPress>();

To get around this we use named registrations

container.Register<FrenchPress>("SixCup");
container.Register<FrenchPress>("ThreeCup");

A named registration overload is available for all the register methods.

See Resolving Named Registrations

Linq Expressions

Linq expressions are a powerful registration tool because they offer alot of flexability and control. The container itself is passed as the input variable, allowing the user to access all the methods in the container. This can be useful to:

The linq expression does not affect lifecycle, and the default rules apply. Below is a flavour of how to implement the above objectives using RegisterExplicit:

//Registers CoffeeRobot with a named dependency
container.RegisterExplicit<CoffeeRobot, CoffeeRobot>(
  c => new CoffeeRobot(c.Resolve<IBrewEquipment>("plunger"))

//Resolve 2 parameter constructor instead of the
//greediest 3 parameter one  
container.RegisterExplicit<CoffeeRobot, CoffeeRobot>(c => new CoffeeRobot(
  c.Resolve<IBrewEquipment>(), 
  c.Resolve<Water>());
                                
//Property injection
container.RegisterExplicit<CoffeeRobot, CoffeeRobot>(c => new CoffeeRobot(
  new Chemex { FilterPaper = 10 }, //<--
  new FilteredWater()));
                                
//Specify that a dependency lives should be 
//resolved from the Parent container
container.RegisterExplicit<CoffeeRobot, CoffeeRobot>(
  c => new CoffeeRobot(c.Parent.Resolve<IBrewEquipment>())                                                           

📝 On Performance Registering with linq expressions generally results in slower resolution than normal registration. The difference is imperceptible, but if you're resolving thousands at a time, then there will be a small difference.

Constructor Selection

📝 By default SmartDi will use the "Greediest Constructor" to create an object. This is the constructor with the most parameters.

You can override this by specifying which constructor to use. For example, lets say our CoffeeRobot has the following constructors:

//SmartDi will default to this one
public CoffeeRobot(IBrewEquipment equipment, Water water, Grinder grinder)

//2 parameters  
public CoffeeRobot(IBrewEquipment equipment, Water water)  

//single parameter  
public CoffeeRobot(IBrewEquipment equipment)

To register with the other constructors:

//2 parameters  
container.Register<CoffeeRobot>(typeof(IBrewEquipment), typeof(Water));

//single parameter  
container.Register<CoffeeRobot>(typeof(IBrewEquipment);

Alternatively, you can decorate the CoffeRobot constructor with Attributes:

//Greediest
public CoffeeRobot(IBrewEquipment equipment, Water water, Grinder grinder)

//SmartDi will use this flagged constructor
[ResolveUsing]
public CoffeeRobot(IBrewEquipment equipment, Water water)  

//single parameter  
public CoffeeRobot(IBrewEquipment equipment)

Finally, one can force register with a specified constructor using Linq Expressions

Open Generic Types

Say we have a generic FrenchPress that can be either Glass or Plastic

public class FrenchPress<T> { }

We can register this as an open generic...

container.RegisterType(typeof(FrenchPress<>));

... and resolve both implementations

var glassPlunger = container.Resolve<FrenchPress<Glass>>();
var plasticPlunger = container.Resolve<FrenchPress<Plastic>>();

IEnumerable

When we register multiple Types with the same Interface, SmartDi is capable of resolving these services as an IEnumerable.

container.Register<IBrewEquipment, FrenchPress>("plunger");
container.Register<IBrewEquipment, Chemex>("pour-over");

//Register the Enumerable
container.Register<IEnumerable<IBrewEquipment>>();

var equipments = container.Resolve<IEnumerable<IBrewEquipment>>();

📝 Order doesn't matter. You can register the Enumerable anywhere.