99 lines
6.3 KiB
Markdown
99 lines
6.3 KiB
Markdown
---
|
|
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>(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<Type> 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<T>() 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<T>() where T : class`**
|
|
Returns the published service instance for interface type `T`. Throws `ArgumentException` if `T` is not published.
|
|
|
|
- **`void Clear<T>() 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<Type> 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<T>()` and `Exists(Type)` return `true` if and only if `Services.ContainsKey(typeof(T))` is `true`. `Get<T>()` will throw if `Exists<T>()` 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<Type, object>` for service storage.
|
|
- `EventManager.EventManager` (from `DTS.Common.Core` namespace) to publish `IServicePublishedEvent` instances via `EventManager.EventManager.Publish<IServicePublishedEvent>(...)`.
|
|
|
|
- **External dependencies**:
|
|
- `System` (for `Type`, `ArgumentException`, `IEnumerable<T>`, etc.).
|
|
|
|
- **Depended upon by**:
|
|
- Any component requiring service discovery (e.g., via `ServiceManager.Get<T>()`).
|
|
- 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<T>()` uses `as T` cast**: If the stored object is not actually assignable to `T` (e.g., due to type mismatch or casting issues), `Get<T>()` 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<T>()` 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<T>()`—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<T>()` followed by `Publish<T>()`.
|
|
- **`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. |