Skip to content
lsoft edited this page Sep 17, 2020 · 32 revisions

Welcome to the DpdtInject wiki!

Purpose

Dpdt is a DI container based on C# Source Generators. Its goal is to remove everything possible from runtime and make resolving process as faster as we can. This is achieved by transferring huge piece of resolving logic to the compilation stage into the source generator.

Status

It's only a proof-of-concept. Nor alpha, neither beta.

Features

  1. Easy-to-read syntax Bind<IA>().To<A>().WithTransientScope().
  2. Generic Get<T> and non generic Get(Type t) resolution.
  3. Single object Get or collection GetAll resolution.
  4. Custom constructor arguments ... Configure(new ConstructorArgument("message", Message)).
  5. Transient, singleton and constant (in progress) scopes.
  6. Additional compile-time safety: First Second
  7. At last, it's very, very fast.

More to come!

Design

Debugging your modules and conditional clauses

Because of source generators are generating new code based on your code, it's impossible to direclty debug your Module code, including its When predicates (because this code is not actually executed at runtime). It's a disadvantage of Dpdt design. For conditional clauses, you need to call another class to obtain an ability to catch a breakpoint:

    public partial class MyModule : DpdtModule
    {
        public override void Load()
        {
            Bind<IA, IA2>()
                .To<A>()
                .WithSingletonScope()
                .When(cc =>
                {
                    //here debugger is NOT working

                    return Debugger.Debug(cc);
                })
                ;
        }
    }

    public static class Debugger
    {
        public static bool Debug(IResolutionContext rc)
        {
            //here debugger is working
            return true;
        }
    }

Syntax

Examples of allowed syntaxes are available in the test project. Please refer that code.

Choosing constructor

Constructor is chosen at the compilation stage based on 2 principles:

  1. Constructors are filtered by ConstructorArgument filter. If no ConstructorArgument has defined, all existing constructors will be taken.
  2. The constructor with the minimum number of parameters is selected to make binding.

Scope

Bind clause with no defined scope raises a question: an author did forgot set a scope or wanted a default scope? We make a decision not to have a default scope and force a user to define a binding.

Singleton

The only one instance of defined type is created. If instance is IDisposable then Dispose method will be invoked at the moment the kernel are disposing.

Transient

Each resolution call results with new instance. Dispose for targets will not be invoked.

Constant

(in progress) Constant scope is a scope when the kernel receive an outside-created object. Its Dispose will not be invoked, because the kernel was not a parent of the constant object.

Conditional binding

Each bind clause may have an additional filter e.g.

            Bind<IA>()
                .To<A>()
                .WithSingletonScope()
                .When(IResolutionContext rc =>
                {
                     condition to resolve
                })
                ;

Please refer unit tests to see the examples. Please note, than any filter makes a resolution process slower (a much slower! 10x slower in compare of unconditional binding!), so use this feature responsibly.

Compile-time safety

Unknown constructor argument

Also, Dpdt will break ongoing compilation if binding has useless ConstructorArgument clause (no constructor with this parameter exists).

Singleton takes transient

Dpdt can detect cases of singleton takes a transient as its dependency, and make signals to the programmer. Please refer to the tests for additional information.

Clone this wiki locally