Lambda Container Registries

Lambda Container registries are .Net classes, which are automatically picked up and used, by the Lambda Container (LC) bootstrapper's assembly scanning component.

The purpose of a registry is to contain type and/or lambda factory registrations for the types published by the assembly/component.

A registry is read and writes it's registrations to a 'registrations' recorder api.

Notice: It is also possible to add registrations before/without reading from the registries. See Setting up the Container for details.

The basic concept

Unlike most other containers, the registration context is the first to be defined. So what does that mean? In this case it means, that you start out defining how the registrations will be managed once applied to the container. You define the lifetime of an instance of a registered type as well as the scope of that rule. Once the context is applied, registrations can be recorded.

Configuring the registrations builder

All registration types use the same registrations builder API. The registrations builder API is provided as argument in the call to the Registry's Read method, and contains a fluent API for building registrations objects.

Once a registrations object is built, the Register<> methods can be chained as showed in the example below. A registrations object only represents the context of how registrations are applied to the LC. You can build as many as you like.
//Entry point called by the registry scanner
public void WriteContentsTo(ILambdaContainerRegistrationsRecorder recorder)
{
    //Record using the selected registration type
    recorder.Record<ITypeMappingRegistrations>(Read);
}

private void Read(ILambdaContainerRegistrationsBuilder<ITypeMappingRegistrations> builder)
{
    //Create a registrations object
    builder
        .WithOutputLifetime(OutputLifetime.ThreadSingleton)
        .WithDisposalScope(DisposalScope.SubScope)
        .Build()
            .Register<IAclass, AClass>()
            .Register<IAnotherClass, AnotherClass>();
            
    //Create another registrations object
    builder.WithOutputLifetime(OutputLifetime.Singleton)
    .Build()
        .Register<IYetAnotherService, AClass>();

}

Registration Builder Settings

  • Output Lifetime
    • Transient: Provides a one-off instance. The caller is responsible for disposing the output after use (if needed)
    • Singleton: The output is a singleton across all threads.
    • ThreadSingleton: Each thread requesting the instance will get their own singleton.
  • Disposal scope
    • Container: Any singleton will be shared with all sub scopes but only disposed with the main container.
    • SubScope: Each sub scope will have it's own instance management of the singleton type and the values will be disposed with the sub scope.

Defaults

  • Disposal scope: SubScope
  • Output lifetime: Transient

Defining registrations

Lambda Container provides 2 different ways of defining how a type should be built:

1: By Type

If you want LC to take care of all of the type buildup, use the ILambdaContainerRegistry<ITypeMappingRegistrations> interface.

Types are registered as "from-to" pairs meaning: GetInstance<from> will return an instance of <to>. This also means that the type <from> can be registered as <to> and in another provider a factory method for <to> can be registered.

If a factory method for <to> is not registered, LC will attempt to create and compile one at runtime (generated function is cached after first resolution of the type). In doing so, the constructor used will be selected by satisfying one of the following conditions:
  • Is public and marked with the LambdaContainerInjectionConstructor attribute
  • Is public and has the largest amount of arguments.
If the selected constructor has any arguments, these are injected through LC as well.

public class Registry : ILambdaContainerRegistry
{
    public void WriteContentsTo(ILambdaContainerRegistrationsRecorder recorder)
    {
        recorder.Record<ITypeMappingRegistrations>(Read);
    }
    public void Read(ILambdaContainerRegistrationsBuilder<ITypeMappingRegistrations> builder)
    {
        builder.Build()
            .Register<IAchild, AChild>()
            .Register<IAclass, AClass>();
        builder.WithOutputLifetime(OutputLifetime.Singleton).Build()
            .Register<IAchild2, AChild>();
    }
}

Injecting dependencies through methods and properties

When the type registration strategy is used, you may benefit from automatic dependency injection through methods and properties (besides the dependencies injected automatically through the constructor). Property and method injection will be part of the compiled factory function generated at runtime.

The following conditions should hold true for properties/methods that you wish to use for dependency injection:
  • They are public
  • They do not expect primitive arguments (int, bool etc.)
  • They are marked as injection targets by using the LambdaContainerInjection attribute.

Example in C#
public class MyClass
    {
...
        [LambdaContainerInjection]
        public IAchild InjectedChild1 { get; set; }
...
        [LambdaContainerInjection]
        public void Inject(PropertyInjectionTestType v)
        {
            _v = v;
        }
...
    }

Example in F#
type public PropertyInjectionTestType() =
    let mutable _t1 = null
    let mutable _t2 = null
    
    [<LambdaContainerInjection>]
    member this.Injected with get () = _t1    
    member this.Injected with set (newVal : TestType) = _t1 <- newVal

    [<LambdaContainerInjection>]
    member this.Inject (t2 : string) =
        _t2 <- t2

2: By lambda factory method

Using the interface ILambdaContainerRegistry<IFactoryRegistrations>, you can register factory methods to construct the types.
As a first argument, the calling LC is passed, so that any constructor arguments may be resolved, if needed, through that. You can use the factory provider to either map an interface to an implementation factory or determine how an implementing class should be instantiated.

public class Registry : ILambdaContainerRegistry
{
    public void WriteContentsTo(ILambdaContainerRegistrationsRecorder recorder)
    {
        recorder.Record<IFactoryRegistrations>(Read);
    }
    public void Read(ILambdaContainerRegistrationsBuilder<IFactoryRegistrations> builder)
    {
        builder
            .Build()
            .Register<IAclass>(lc=>new AClass(lc.GetInstance<IAchild>()));
    }
}

3: By Convention

TODO: Will arrive soon
Until it arrives, please inspect: https://lambdacontainer.codeplex.com/SourceControl/latest#src/LambdaContainer.Core/LambdaContainer.Core.Tests/ConventionRegistrationsTest.fs

Last edited Jul 4 at 6:53 AM by Remboel, version 33