Files
DP44/enriched-partialglm/Common/DTS.Common.Core/ServiceManager.md
2026-04-17 14:55:32 -04:00

68 lines
5.5 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-16T11:43:09.898125+00:00"
model: "zai-org/GLM-5-FP8"
schema_version: 1
sha256: "a01f75e5b963dd9f"
---
# Documentation: DTS.Common.Core.ServiceManager
## 1. Purpose
This module implements a service locator pattern via a static `ServiceManager` class. It allows components to publish implementations of interfaces (services) into a global container and retrieve them elsewhere without direct coupling to the implementation. It acts as a runtime registry for singleton services, supporting publication, retrieval, existence checks, and un-publication, while broadcasting state changes via an event system.
## 2. Public Interface
### `IServicePublishedEvent` (Interface)
Defines the contract for events fired when a service state changes.
* `Type ServiceType { get; }` — Gets the type of the service being published or unpublished.
* `bool IsPublished { get; }` — Returns `true` if the service is being published; `false` if it is being unpublished.
### `ServicePublishedEvent` (Class)
Concrete implementation of `IServicePublishedEvent`.
* `Type ServiceType { get; internal set; }` — The service type. The setter is `internal`.
* `bool IsPublished { get; internal set; }` — The publication state. The setter is `internal`.
### `ServiceManager` (Static Class)
The static gateway for managing service registration.
* `public static void Publish(T item) where T : class`
* Registers a service implementation `item` under the interface type `T`.
* Throws `ArgumentException` if `T` is already registered.
* `public static void Publish(object item, IEnumerable<Type> interfaceList, bool skipPublishedInterfaces)`
* Registers a single object `item` as the implementation for multiple interfaces defined in `interfaceList`.
* If `skipPublishedInterfaces` is `false`, throws `ArgumentException` if any interface in the list is already registered.
* If `skipPublishedInterfaces` is `true`, silently skips already registered interfaces.
* `public static bool Exists() where T : class`
* Returns `true` if a service of interface type `T` is currently registered; otherwise `false`.
* `public static bool Exists(Type t)`
* Non-generic overload. Returns `true` if the specified `Type t` is registered; otherwise `false`.
* `public static T Get() where T : class`
* Retrieves the registered service implementation for interface type `T`.
* Throws `ArgumentException` if `T` has not been published.
* `public static void Clear() where T : class`
* Un-registers the service for interface type `T`. Fires an "unpublished" event.
* `public static void Clear(IEnumerable<Type> interfaceList)`
* Un-registers all services for the types specified in `interfaceList`. Fires "unpublished" events for each removed service.
## 3. Invariants
* **Uniqueness:** The `ServiceManager` enforces a one-to-one mapping between a `Type` and a service instance. A `Type` cannot be published twice unless the previous instance is cleared or the batch publish method is used with `skipPublishedInterfaces` set to `true`.
* **Reference Types Only:** All generic methods (`Publish`, `Exists`, `Get`, `Clear`) constrain the type parameter `T` to `class`.
* **Event Ordering:** When clearing a service, the `IServicePublishedEvent` (with `IsPublished = false`) is fired **before** the service is removed from the internal dictionary.
* **Exception Consistency:** Both `Publish<T>` and `Get<T>` throw `ArgumentException` (not a custom exception type) for invalid states (already exists / not found).
## 4. Dependencies
* **Dependencies (Internal):**
* `ServiceManager` depends on `ServicePublishedEvent` to create event payloads.
* `ServicePublishedEvent` depends on `IServicePublishedEvent`.
* **Dependencies (External):**
* `ServiceManager` depends on `EventManager.EventManager`. The private method `SendServicePublishedEvent` calls `EventManager.EventManager.Publish<IServicePublishedEvent>(...)`. The location/assembly of `EventManager` is not defined in the provided source but is required for this module to compile and run.
* **Standard Libraries:** `System`, `System.Collections.Generic`.
## 5. Gotchas
* **Thread Safety:** The internal storage `Dictionary<Type, object> Services` uses standard `System.Collections.Generic.Dictionary`. There are no locks or synchronization mechanisms in `ServiceManager`. This module is **not thread-safe**. Concurrent calls to `Publish`, `Get`, or `Clear` may result in race conditions or corruption.
* **Event Manager Coupling:** The `ServiceManager` is tightly coupled to a specific `EventManager.EventManager` class. If that event manager is not initialized or accessible, the `Publish` and `Clear` methods will fail at runtime when attempting to broadcast events.
* **Silent Failures in Batch Publish:** When calling `Publish(object, IEnumerable<Type>, bool)` with `skipPublishedInterfaces` set to `true`, the method will silently ignore interfaces that are already registered. This could lead to stale service instances remaining active if the caller assumed the new `item` would replace them.
* **Clear Event Timing:** Because the "unpublished" event fires before the item is removed from the dictionary, an event subscriber handling `IServicePublishedEvent` with `IsPublished == false` could technically still call `ServiceManager.Exists(type)` and receive `true` inside their event handler.