--- source_files: - Common/DTS.Common.Core/ServiceManager/IServicePublishedEvent.cs - Common/DTS.Common.Core/ServiceManager/ServicePublishedEvent.cs - Common/DTS.Common.Core/ServiceManager/ServiceManager.cs generated_at: "2026-04-16T02:06:21.064356+00:00" model: "Qwen/Qwen3-Coder-Next-FP8" schema_version: 1 sha256: "a01f75e5b963dd9f" --- # ServiceManager ## Documentation: ServiceManager Module ### 1. Purpose The `ServiceManager` module provides a centralized registry for singleton service implementations, enabling decoupled service discovery and lifecycle management. Components can publish implementations of service interfaces (e.g., `IMyService`) and other components can retrieve them by interface type without knowledge of the concrete publisher. It also emits `IServicePublishedEvent` notifications via `EventManager` whenever services are published or unpublished, supporting reactive integration patterns. --- ### 2. Public Interface #### `ServiceManager` (static class) - **`void Publish(T item) where T : class`** Publishes a singleton service implementation `item` for interface type `T`. Throws `ArgumentException` if `T` is already published. Fires a `ServicePublishedEvent` with `IsPublished = true`. - **`void Publish(object item, IEnumerable interfaceList, bool skipPublishedInterfaces)`** Publishes `item` for each interface type in `interfaceList`. If `skipPublishedInterfaces` is `false`, throws `ArgumentException` on encountering an already-published interface; otherwise, skips it. Fires `ServicePublishedEvent` for newly published interfaces. - **`bool Exists() where T : class`** Returns `true` if interface type `T` has a published service; `false` otherwise. - **`bool Exists(Type t)`** Returns `true` if interface type `t` has a published service; `false` otherwise. - **`T Get() where T : class`** Returns the published service instance for interface type `T`. Throws `ArgumentException` if `T` is not published. - **`void Clear() where T : class`** Unpublishes the service for interface type `T`. Fires a `ServicePublishedEvent` with `IsPublished = false` before removal. No-op if `T` is not published. - **`void Clear(IEnumerable interfaceList)`** Unpublishes services for all interface types in `interfaceList`. Fires `ServicePublishedEvent` with `IsPublished = false` for each successfully removed service. #### `IServicePublishedEvent` (interface) - **`Type ServiceType { get; }`** Read-only property returning the `Type` of the service interface being published/unpublished. - **`bool IsPublished { get; }`** Read-only property: `true` if the event indicates publication; `false` if unpublishing. #### `ServicePublishedEvent` (class) - **`Type ServiceType { get; internal set; }`** Gets or sets (internally) the service interface `Type`. - **`bool IsPublished { get; internal set; }`** Gets or sets (internally) the publication status. --- ### 3. Invariants - **Uniqueness per interface**: Each interface type `T` may have at most one published service instance. Attempting to publish a second implementation for the same interface throws `ArgumentException` (unless `skipPublishedInterfaces = true` in the bulk `Publish` overload). - **Consistency of `Exists`/`Get`/`Clear`**: `Exists()` and `Exists(Type)` return `true` if and only if `Services.ContainsKey(typeof(T))` is `true`. `Get()` will throw if `Exists()` is `false`. - **Event emission order**: For `Publish`/`Clear` operations, the `ServicePublishedEvent` is fired *before* the service is added/removed from the internal dictionary. Specifically: - `Publish` → `Services.Add(...)` → `SendServicePublishedEvent(...)` - `Clear` → `SendServicePublishedEvent(...)` → `Services.Remove(...)` - **No null services**: The `Services` dictionary stores only non-null references. `Publish` accepts any `class`, but the dictionary does not explicitly validate non-nullability beyond standard dictionary behavior. --- ### 4. Dependencies - **Internal dependencies**: - `System.Collections.Generic.Dictionary` for service storage. - `EventManager.EventManager` (from `DTS.Common.Core` namespace) to publish `IServicePublishedEvent` instances via `EventManager.EventManager.Publish(...)`. - **External dependencies**: - `System` (for `Type`, `ArgumentException`, `IEnumerable`, etc.). - **Depended upon by**: - Any component requiring service discovery (e.g., via `ServiceManager.Get()`). - Event subscribers listening for `IServicePublishedEvent` to react to service lifecycle changes. --- ### 5. Gotchas - **No thread-safety guarantees**: The `Services` dictionary is not thread-safe. Concurrent `Publish`/`Clear`/`Get` calls may cause race conditions or exceptions (e.g., `InvalidOperationException` during enumeration or dictionary mutation). - **`Publish(object, ...)` allows multiple interfaces per instance**: The same object instance may be published under multiple interface types, but this is not validated beyond the existence check per interface. - **`Get()` uses `as T` cast**: If the stored object is not actually assignable to `T` (e.g., due to type mismatch or casting issues), `Get()` returns `null` *without throwing*—but only if the key exists. However, the current implementation first checks `ContainsKey`, so `null` return is impossible *if* the stored object is non-null and correctly typed. If the stored object is `null`, `ContainsKey` would still be `true`, and `Get()` would return `null`. This edge case is not explicitly guarded against. - **Event emission timing**: Subscribers receive `ServicePublishedEvent` *before* the service is available (for `Publish`) or *after* it is removed (for `Clear`). This may cause race conditions if subscribers immediately call `Get()`—they will succeed for `Publish`, but fail for `Clear`. - **No support for service versioning or replacement**: Once published, a service cannot be replaced without explicit `Clear()` followed by `Publish()`. - **`internal set` on `ServicePublishedEvent` properties**: While the properties are public, their setters are `internal`, meaning only code within the same assembly can construct/modify `ServicePublishedEvent` instances. This is consistent with the design but limits extensibility. None identified beyond the above.