mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-24 20:34:29 +08:00
- 新增 RunInBatch() 方法支持批处理通知折叠 - 添加 Undo()/Redo() 基于历史缓冲区的状态回退前进功能 - 实现 TimeTravelTo() 跳转到指定历史索引的能力 - 提供 ClearHistory() 以当前状态重置历史锚点的功能 - 支持可选历史缓冲区、撤销/重做和时间旅行功能 - 添加可选批处理通知折叠机制 - 实现多态 action 匹配(基类/接口)支持 - 在诊断信息中增加历史游标和批处理状态 - StoreBuilder 新增 WithHistoryCapacity() 和 WithActionMatching() 配置方法 - 优化 reducer 注册支持全局序号以实现稳定排序 - 实现多态匹配时的类型继承距离计算 - 添加批处理嵌套支持和状态通知延迟机制
118 lines
4.9 KiB
C#
118 lines
4.9 KiB
C#
using GFramework.Core.Abstractions.Events;
|
|
using GFramework.Core.Abstractions.StateManagement;
|
|
using GFramework.Core.Events;
|
|
using GFramework.Core.StateManagement;
|
|
|
|
namespace GFramework.Core.Extensions;
|
|
|
|
/// <summary>
|
|
/// 为 Store 提供到 EventBus 的兼容桥接扩展。
|
|
/// 该扩展面向旧模块渐进迁移场景,使现有事件消费者可以继续观察 Store 的 action 分发和状态变化。
|
|
/// </summary>
|
|
public static class StoreEventBusExtensions
|
|
{
|
|
/// <summary>
|
|
/// 将 Store 的 dispatch 和状态变化同时桥接到 EventBus。
|
|
/// dispatch 事件会逐次发布;状态变化事件会复用 Store 自身的通知折叠语义,因此批处理中只发布最终状态。
|
|
/// </summary>
|
|
/// <typeparam name="TState">状态树的根状态类型。</typeparam>
|
|
/// <param name="store">源 Store。</param>
|
|
/// <param name="eventBus">目标事件总线。</param>
|
|
/// <param name="publishDispatches">是否发布每次 action 分发事件。</param>
|
|
/// <param name="publishStateChanges">是否发布状态变化事件。</param>
|
|
/// <returns>用于拆除桥接的句柄。</returns>
|
|
public static IUnRegister BridgeToEventBus<TState>(
|
|
this Store<TState> store,
|
|
IEventBus eventBus,
|
|
bool publishDispatches = true,
|
|
bool publishStateChanges = true)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(store);
|
|
ArgumentNullException.ThrowIfNull(eventBus);
|
|
|
|
IUnRegister? dispatchBridge = null;
|
|
IUnRegister? stateBridge = null;
|
|
|
|
if (publishDispatches)
|
|
{
|
|
dispatchBridge = store.BridgeDispatchesToEventBus(eventBus);
|
|
}
|
|
|
|
if (publishStateChanges)
|
|
{
|
|
stateBridge = store.BridgeStateChangesToEventBus(eventBus);
|
|
}
|
|
|
|
return new DefaultUnRegister(() =>
|
|
{
|
|
dispatchBridge?.UnRegister();
|
|
stateBridge?.UnRegister();
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将 Store 的每次 dispatch 结果桥接到 EventBus。
|
|
/// 该桥接通过中间件实现,因此即使某次分发未改变状态,也会发布对应的 dispatch 事件。
|
|
/// </summary>
|
|
/// <typeparam name="TState">状态树的根状态类型。</typeparam>
|
|
/// <param name="store">源 Store。</param>
|
|
/// <param name="eventBus">目标事件总线。</param>
|
|
/// <returns>用于移除 dispatch 桥接中间件的句柄。</returns>
|
|
public static IUnRegister BridgeDispatchesToEventBus<TState>(this Store<TState> store, IEventBus eventBus)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(store);
|
|
ArgumentNullException.ThrowIfNull(eventBus);
|
|
|
|
return store.RegisterMiddleware(new DispatchEventBusMiddleware<TState>(eventBus));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将 Store 的状态变化桥接到 EventBus。
|
|
/// 该桥接复用 Store 的订阅通知语义,因此只会在状态真正变化时发布事件。
|
|
/// </summary>
|
|
/// <typeparam name="TState">状态树的根状态类型。</typeparam>
|
|
/// <param name="store">源 Store。</param>
|
|
/// <param name="eventBus">目标事件总线。</param>
|
|
/// <returns>用于移除状态变化桥接的句柄。</returns>
|
|
public static IUnRegister BridgeStateChangesToEventBus<TState>(this IReadonlyStore<TState> store,
|
|
IEventBus eventBus)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(store);
|
|
ArgumentNullException.ThrowIfNull(eventBus);
|
|
|
|
return store.Subscribe(state =>
|
|
eventBus.Send(new StoreStateChangedEvent<TState>(state, DateTimeOffset.UtcNow)));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 用于把 dispatch 结果桥接到 EventBus 的内部中间件。
|
|
/// 选择中间件而不是改写 Store 核心提交流程,是为了把兼容层成本保持在可选扩展中。
|
|
/// </summary>
|
|
/// <typeparam name="TState">状态树的根状态类型。</typeparam>
|
|
private sealed class DispatchEventBusMiddleware<TState>(IEventBus eventBus) : IStoreMiddleware<TState>
|
|
{
|
|
/// <summary>
|
|
/// 目标事件总线。
|
|
/// </summary>
|
|
private readonly IEventBus _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
|
|
|
|
/// <summary>
|
|
/// 执行后续 dispatch 管线,并在结束后把分发结果发送到 EventBus。
|
|
/// </summary>
|
|
/// <param name="context">当前分发上下文。</param>
|
|
/// <param name="next">后续管线。</param>
|
|
public void Invoke(StoreDispatchContext<TState> context, Action next)
|
|
{
|
|
next();
|
|
|
|
var dispatchRecord = new StoreDispatchRecord<TState>(
|
|
context.Action,
|
|
context.PreviousState,
|
|
context.NextState,
|
|
context.HasStateChanged,
|
|
context.DispatchedAt);
|
|
|
|
_eventBus.Send(new StoreDispatchedEvent<TState>(dispatchRecord));
|
|
}
|
|
}
|
|
} |