Which constructs support component injection?

← DI Validation · Ref: Q668

Injection - a variable declared with the '!' suffix - can be written in components, services, classes, traits, functions, dynamic classes and dynamic functions, as a property or inside a method or function body. A program can inject too, once it is linked to an application. A record cannot (E08160): it holds data only, and its fields take part in its generated equality, hashing and copying.

WHAT THE COMPILER CHECKS

- the injected type must be an abstract component (E08160 for any other genus, E08150 for a concrete component)
- the variable must not be a parameter or a return value (E08160)
- the scope must not be pure (E08140)
- a program that injects must declare 'with application of AppName' (E08220)
- that application must register every type the program injects (E08210)

INSIDE A DYNAMIC CLASS

The injection belongs to the dynamic class, not to the class, service or program it is written in. The DI checks follow the calls to wherever the dynamic class is created; a dynamic class in a program's body is satisfied by that program's application.

KEEP IT AT THE TOP

Injection ties code to the application's wiring, so inject sparingly and as high up as possible.

See Q111 for component basics. See Q233 for injection in a dynamic class. See Q666 for pure context. See Q667 for abstract injection. See Q672 for program-application linking.

Example

defines module qa.divalidation.injectablecontexts

  defines component

    <?-
      Abstract notification service contract.
    -?>
    NotificationService as abstract

      sendNotification() as abstract
        -> recipient as String

      default operator ?

    <?-
      Email-based notification implementation.
    -?>
    EmailNotifier extends NotificationService

      override sendNotification()
        -> recipient as String
        stdout <- Stdout()
        stdout.println(`Email sent to ${recipient}`)

      default operator ?

  defines application

    OrderApp
      register EmailNotifier() as NotificationService

  defines program

    <?-
      Program with injection: 'with application of' enables DI.
      This is the correct injectable context for programs.
    -?>
    InjectableContextDemo() with application of OrderApp
      stdout <- Stdout()

      notifier as NotificationService!
      notifier.sendNotification("demo-recipient")

      stdout.println("Injection works in programs linked to applications")
      stdout.println("Components and services also support injection")

Common mistakes

E08220 — A program that injects must name the application that does the wiring - without 'with application of', nothing says which concrete component to inject. See ek9 -h E08220 for details.

Incorrect:

InjectableContextDemo()

Correct:

InjectableContextDemo() with application of OrderApp
Other ways to ask this
  • What is E08160 injection not possible in context?
  • Can I inject in functions or records?
  • Where can I use the ! injection suffix?

Coming from another language?

Java: Spring @Autowired works in any managed bean. Python: inject anywhere with frameworks. Go: manual wiring. Rust: no DI framework. EK9: '!' can be written in any construct except a record, but only an abstract component can be injected, and a program must name the application that does the wiring.

Keywords: injection, constant, validate, component, program, service, context, dynamic class, record, registration, E08160, inject, DI