-
Notifications
You must be signed in to change notification settings - Fork 24
Description
Summary
Introduce a new optional facade interface to surface change notifications whenever a resource reports changes via Resource.RaiseResourceChanged(), and whenever a resource is changed through the ResourceManagement facade (e.g., Modify). This mirrors existing ResourceAdded, ResourceRemoved, and CapabilitiesChanged notifications and enables consumers to react to runtime changes without polling. The change is non-breaking: the existing IResourceManagement
interface remains unchanged; adopters can opt-in via an additional interface.
Motivation
- Consumers can currently subscribe to
ResourceAdded
,ResourceRemoved
, andCapabilitiesChanged
onIResourceManagement
, but there is no unified notification for general resource changes (property updates inside Resource subclasses). - Resources already signal changes through Resource.RaiseResourceChanged(), and ResourceManager listens to resource events internally. Surfacing a change event at the facade level completes the event model for clients (UI, orchestration, monitoring).
Proposal
Relevant existing components:
- IResourceManagement
- Provides GetResource(s), Create, Read, Modify, Delete, and events: ResourceAdded, ResourceRemoved, CapabilitiesChanged.
- Resource
- protected void RaiseResourceChanged() triggers Changed?.Invoke(this, EventArgs.Empty).
- Resources management module (implementation):
- ResourceManager:
- Registers to resource events (RegisterEvents)
- Handles changes (OnResourceChanged)
- Saves and links references (Save, LinkReferences)
- Handles auto-save changes (OnAutoSaveCollectionChanged).
- ResourceManagementFacade
- Implements IResourceManagement.
- Bridges ResourceAdded, ResourceRemoved, CapabilitiesChanged from ResourceManager to facade subscribers.
- ModuleController
- Exposes facades to consumers (IResourceManagement, IResourceTypeTree).
- ResourceManager:
Implementation steps
- Add a new optional facade interface in the abstraction layer
namespace Moryx.AbstractionLayer.Resources
{
public interface IResourceManagementChanges
{
/// <summary>
/// Raised when a resource reports a change (via Resource.RaiseResourceChanged)
/// or is changed via the ResourceManagement facade (e.g. Modify).
/// </summary>
event EventHandler<IResource> ResourceChanged;
}
}
- Raise
ResourceChanged
from the resources management module
-
Extend the internal ResourceManager to surface a
ResourceChanged
event:- Add: event EventHandler ResourceChanged;
- This is internal and thus non-breaking for public consumers.
-
Implement event propagation in ResourceManager :
- Ensure subscription to resource.Changed (already in RegisterEvents) triggers a save and then raises ResourceChanged after successful persistence.
- Also raise ResourceChanged after Modify and any auto-save collection updates that represent a change, to cover changes initiated via the facade.
internal void OnResourceChanged(object sender, EventArgs e)
{
var resource = (Resource)sender;
// Persist changes, update relations, etc.
Save(resource);
// Notify listeners about the change:
// ToDo: Verify whether raising the event should be put into the Save method
ResourceChanged?.Invoke(this, resource);
}
// Wherever Modify saves a resource (or after OnAutoSaveCollectionChanged detected changes):
ResourceChanged?.Invoke(this, resource);
- Implement the new interface in the facade
- Implement
IResourceManagementChanges
- Add the event and wire it to ResourceManager.ResourceChanged on Activate; unsubscribe on Deactivate.
public class ResourceManagementFacade : FacadeBase, IResourceManagement, IResourceManagementChanges
{
public event EventHandler<IResource> ResourceChanged;
public override void Activate()
{
base.Activate();
_resourceManager.ResourceChanged += OnResourceChanged;
// Existing subscriptions: ResourceAdded, ResourceRemoved, CapabilitiesChanged
}
public override void Deactivate()
{
_resourceManager.ResourceChanged -= OnResourceChanged;
base.Deactivate();
}
private void OnResourceChanged(object sender, IResource resource)
{
ResourceChanged?.Invoke(this, resource);
}
}
- Ensure the ModuleController exposes the new facade interface
- Verify the facade container publishes ResourceManagementFacade under both IResourceManagement and IResourceManagementChanges.
Behavior
-
ResourceChanged
is raised:- When a resource instance calls Resource.RaiseResourceChanged().
- When a resource is modified via IResourceManagement.Modify (after persistence).
- Optionally when auto-saved collection references change (post-persistence), if not already covered by resource.Changed.
-
ResourceChanged
is not a replacement for lifecycle events:ResourceAdded
andResourceRemoved
continue to denote add/remove operations.CapabilitiesChanged
remains distinct for capability updates.- Do not raise
ResourceChanged
redundantly for add/remove unless a separate change occurs.
Example usage
var facade = container.Resolve();
if (facade is IResourceManagementChanges changeEvents)
{
changeEvents.ResourceChanged += (s, resource) =>
{
// React to resource change (e.g., update UI, refresh cache)
Console.WriteLine($"Resource changed: {resource.Name} (Id={resource.Id})");
};
}
Test considerations
- Verify that a Resource subclass invoking RaiseResourceChanged results in a ResourceChanged event on the facade.
- Use DefaultTestResource (src/Tests/Moryx.Resources.Management.Tests/ResourceEntityAccessorTests.cs), which can trigger RaiseResourceChanged after a property update.
- Verify that IResourceManagement.Modify triggers ResourceChanged after persistence.
- Verify that changes detected via OnAutoSaveCollectionChanged are surfaced.
- Ensure events are not raised for Add/Remove unless an actual change occurs in addition to lifecycle.
Acceptance criteria
- New interface IResourceManagementChanges is available to consumers and implemented by ResourceManagementFacade.
- Resource changes (internal RaiseResourceChanged and facade Modify) result in ResourceChanged events observable via the facade.
- No breaking changes to IResourceManagement or other public contracts.
- Existing events (ResourceAdded, ResourceRemoved, CapabilitiesChanged) continue to function unchanged.
- Tests cover both direct resource change and facade-driven modify scenarios.