Skip to content

Yohira is an experimental project to port ASP.NET Core to TypeScript.

License

Notifications You must be signed in to change notification settings

ycanardeau/yohira

Repository files navigation

Yohira

Yohira is an experimental project to port ASP.NET Core to TypeScript.

Installation

npm i yohira

This is a TypeScript port of Microsoft.Extensions.DependencyInjection.

Add interfaces

import { ServiceLifetime } from 'yohira';

interface IReportServiceLifetime {
    readonly id: string;
    readonly lifetime: ServiceLifetime;
}

const IExampleTransientService = Symbol.for('IExampleTransientService');
interface IExampleTransientService extends IReportServiceLifetime {
    readonly lifetime: ServiceLifetime.Transient;
}

const IExampleScopedService = Symbol.for('IExampleScopedService');
interface IExampleScopedService extends IReportServiceLifetime {
    readonly lifetime: ServiceLifetime.Scoped;
}

const IExampleSingletonService = Symbol.for('IExampleSingletonService');
interface IExampleSingletonService extends IReportServiceLifetime {
    readonly lifetime: ServiceLifetime.Singleton;
}

Add default implementations

import { randomUUID } from 'node:crypto';

class ExampleTransientService implements IExampleTransientService {
    readonly id = randomUUID();
    readonly lifetime = ServiceLifetime.Transient;
}

class ExampleScopedService implements IExampleScopedService {
    readonly id = randomUUID();
    readonly lifetime = ServiceLifetime.Scoped;
}

class ExampleSingletonService implements IExampleSingletonService {
    readonly id = randomUUID();
    readonly lifetime = ServiceLifetime.Singleton;
}

Add a service that requires DI

import { inject } from 'yohira';

function logService<T extends IReportServiceLifetime>(
    name: symbol,
    service: T,
    message: string,
): void {
    console.log(`    ${Symbol.keyFor(name)}: ${service.id} (${message})`);
}

class ServiceLifetimeReporter {
    constructor(
        @inject(IExampleTransientService)
        private readonly transientService: IExampleTransientService,

        @inject(IExampleScopedService)
        private readonly scopedService: IExampleScopedService,

        @inject(IExampleSingletonService)
        private readonly singletonService: IExampleSingletonService,
    ) {}

    reportServiceLifetimeDetails(lifetimeDetails: string): void {
        console.log(lifetimeDetails);

        logService(
            IExampleTransientService,
            this.transientService,
            'Always different',
        );
        logService(
            IExampleScopedService,
            this.scopedService,
            'Changes only with lifetime',
        );
        logService(
            IExampleSingletonService,
            this.singletonService,
            'Always the same',
        );
    }
}

Register services for DI

import {
    IServiceProvider,
    addScopedCtor,
    addSingletonCtor,
    addTransientCtor,
    createDefaultBuilder,
    createScope,
    getRequiredService,
    runApp,
} from 'yohira';

function exemplifyServiceLifetime(
    hostProvider: IServiceProvider,
    lifetime: string,
): void {
    const serviceScope = createScope(hostProvider);
    const provider = serviceScope.serviceProvider;
    const reporter1 = getRequiredService<ServiceLifetimeReporter>(
        provider,
        Symbol.for('ServiceLifetimeReporter'),
    );
    reporter1.reportServiceLifetimeDetails(
        `${lifetime}: Call 1 to getRequiredService<ServiceLifetimeReporter>` +
            "(provider, Symbol.for('ServiceLifetimeReporter'))",
    );

    console.log('...');

    const reporter2 = getRequiredService<ServiceLifetimeReporter>(
        provider,
        Symbol.for('ServiceLifetimeReporter'),
    );
    reporter2.reportServiceLifetimeDetails(
        `${lifetime}: Call 2 to getRequiredService<ServiceLifetimeReporter>` +
            "(provider, Symbol.for('ServiceLifetimeReporter'))",
    );

    console.log('\n');

    serviceScope.dispose();
}

const host = createDefaultBuilder()
    .configureServices((context, services) => {
        addTransientCtor(
            services,
            IExampleTransientService,
            ExampleTransientService,
        );
        addScopedCtor(services, IExampleScopedService, ExampleScopedService);
        addSingletonCtor(
            services,
            IExampleSingletonService,
            ExampleSingletonService,
        );
        addTransientCtor(
            services,
            Symbol.for('ServiceLifetimeReporter'),
            ServiceLifetimeReporter,
        );
    })
    .build();

exemplifyServiceLifetime(host.services, 'Lifetime 1');
exemplifyServiceLifetime(host.services, 'Lifetime 2');

await runApp(host);

See also

References