---
title: "OP-03: Register a pingable service"
summary: Dual registration of a service under its own type and IPingableService.
status: draft
owner: SAIF Platform Team
last_reviewed: 2026-10-02
library: SAIF.NetCore.PingableService
reference_type: package
operation_id: OP-03
trigger: Host startup code calls AddPingableTransient, AddPingableScoped, or AddPingableSingleton.
---

# OP-03: Register a pingable service

| Field | Value |
| --- | --- |
| Operation ID | OP-03 |
| Trigger | Host startup code calls `AddPingableTransient<TService, TImplementation>()`, `AddPingableScoped<TService, TImplementation>()`, or `AddPingableSingleton<TService, TImplementation>()`. |
| Inputs | `IServiceCollection`; generic type arguments `TService`, `TImplementation`. |
| Outputs | Two DI registrations on the same `IServiceCollection`. |
| Primary components | `IServiceCollectionExtensions` |
| Entry point | `AddPingableTransient` / `AddPingableScoped` / `AddPingableSingleton` |

## Steps
1. The generic constraints `where TService : class, IPingableService` and `where TImplementation : class, TService, IPingableService` are checked at compile time, so `TService` itself must be (or extend) `IPingableService`, and `TImplementation` must implement both `TService` and `IPingableService` ([BR-11](../business-rules.md#br-11-pingable-registration-requires-tservice-to-extend-ipingableservice)).
2. Register `TImplementation` under `TService` with the requested lifetime (transient, scoped, or singleton) — e.g. `services.AddTransient<TService, TImplementation>()`.
3. Register the forwarding factory `services.AddTransient<IPingableService, TImplementation>(x => (TImplementation)x.GetRequiredService<TService>())` (substituting `AddScoped`/`AddSingleton` for the other two methods), with the same lifetime as step 2, so `Provider.GetServices<IPingableService>()` in [OP-01](op-01-ping-the-api.md) step 3 can find it. The factory does not create a second instance — it resolves `TService` from the same provider/scope and casts the result to `TImplementation` ([BR-15](../business-rules.md#br-15-ipingableservice-resolution-casts-through-tservice-registration)).

## Process flow

```mermaid
flowchart TD
    A[Host calls AddPingableTransient/Scoped/Singleton] --> B["Compile-time constraints: TService : IPingableService;<br>TImplementation : TService, IPingableService"]
    B --> C[Register TImplementation under TService]
    C --> D["Register IPingableService with forwarding factory:<br>x =&gt; (TImplementation)x.GetRequiredService&lt;TService&gt;()"]
    D --> E[Both registrations share the same lifetime]
```

## Failure and edge-case behavior
- For Transient, the forwarding factory still produces a fresh `TImplementation` instance per resolution (each `GetRequiredService<TService>()` call creates a new one), so `TService` and `IPingableService` resolutions are independent instances, consistent with transient semantics.
- For Scoped, every resolution of `IPingableService` within a given scope shares the identical `TImplementation` instance that `TService` resolves to in that same scope — the forwarding factory already provides this identity sharing; a host never needs to add its own factory for it to work.
- For Singleton, every resolution of `IPingableService` for the lifetime of the application shares the identical singleton instance that `TService` resolves to.
- If a caller replaces the `TService` registration independently (bypassing `AddPingable*`, e.g. by calling `services.AddScoped<TService, OtherImplementation>()` afterward), the forwarding factory's cast `(TImplementation)x.GetRequiredService<TService>()` fails at ping time with an `InvalidCastException`, which surfaces as a 500 via [BR-05](../business-rules.md#br-05-resolution-and-construction-failures-count-as-ping-failures) ([BR-15](../business-rules.md#br-15-ipingableservice-resolution-casts-through-tservice-registration)).

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [Service registration](../service-registration.md)

## Sources
- `src/SAIF.NetCore.PingableService/SAIF.NetCore.PingableService/Extensions/IServiceCollectionExtensions.cs`
