mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
feat(router): 添加场景和UI路由的Around中间件支持
- 在SceneRouterBase和UiRouterBase中集成管道执行Around处理器 - 实现场景过渡管道SceneTransitionPipeline的Around处理器注册和执行功能 - 实现UI过渡管道UiTransitionPipeline的Around处理器注册和执行功能 - 添加ISceneAroundTransitionHandler和IUiAroundTransitionHandler接口定义 - 扩展SceneTransitionPhases和UITransitionPhases枚举支持Around阶段 - 实现Around处理器的优先级排序和中间件链构建机制 - 添加Around处理器的超时控制和异常处理机制
This commit is contained in:
parent
6f61ff55aa
commit
53c2ee4ef3
@ -34,9 +34,16 @@ public enum SceneTransitionPhases
|
||||
/// </summary>
|
||||
AfterChange = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 中间件阶段(阻塞执行)。
|
||||
/// 用于包裹整个场景切换过程的逻辑,如性能监控、事务管理、权限验证等。
|
||||
/// Around 处理器在变更前后都会执行,可以控制是否继续执行变更。
|
||||
/// </summary>
|
||||
Around = 4,
|
||||
|
||||
/// <summary>
|
||||
/// 所有阶段的组合标志。
|
||||
/// 表示处理器适用于场景切换的所有阶段。
|
||||
/// </summary>
|
||||
All = BeforeChange | AfterChange
|
||||
All = BeforeChange | AfterChange | Around
|
||||
}
|
||||
@ -19,7 +19,14 @@ public enum UiTransitionPhases
|
||||
AfterChange = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 所有阶段,Handler将在BeforeChange和AfterChange阶段都执行
|
||||
/// 中间件阶段,支持包裹整个变更过程的逻辑(阻塞执行)
|
||||
/// 适用于:性能监控、事务管理、权限验证、日志记录开始/结束等需要控制流程的操作
|
||||
/// Around 处理器在变更前后都会执行,可以决定是否继续执行后续逻辑
|
||||
/// </summary>
|
||||
All = BeforeChange | AfterChange
|
||||
Around = 4,
|
||||
|
||||
/// <summary>
|
||||
/// 所有阶段,Handler将在BeforeChange、AfterChange和Around阶段都执行
|
||||
/// </summary>
|
||||
All = BeforeChange | AfterChange | Around
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2026 GeWuYou
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
namespace GFramework.Game.Abstractions.scene;
|
||||
|
||||
/// <summary>
|
||||
/// 场景切换中间件处理器接口,支持包裹整个变更过程的逻辑。
|
||||
/// Around 处理器在变更前后都会执行,可以控制是否继续执行变更。
|
||||
/// 适用于:性能监控、事务管理、权限验证、日志记录等横切关注点。
|
||||
/// </summary>
|
||||
public interface ISceneAroundTransitionHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取处理器的执行优先级。
|
||||
/// 数值越小优先级越高,越先执行(外层)。
|
||||
/// 建议范围:-1000 到 1000。
|
||||
/// </summary>
|
||||
int Priority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 判断处理器是否应该处理当前场景过渡事件。
|
||||
/// </summary>
|
||||
/// <param name="event">场景过渡事件。</param>
|
||||
/// <returns>如果应该处理则返回 true,否则返回 false。</returns>
|
||||
bool ShouldHandle(SceneTransitionEvent @event);
|
||||
|
||||
/// <summary>
|
||||
/// 执行中间件逻辑。
|
||||
/// </summary>
|
||||
/// <param name="event">场景过渡事件,包含切换的上下文信息。</param>
|
||||
/// <param name="next">下一个中间件或实际操作的委托。调用此委托以继续执行流程。</param>
|
||||
/// <param name="cancellationToken">取消令牌,用于支持操作取消。</param>
|
||||
/// <returns>表示处理操作完成的异步任务。</returns>
|
||||
Task HandleAsync(
|
||||
SceneTransitionEvent @event,
|
||||
Func<Task> next,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
namespace GFramework.Game.Abstractions.ui;
|
||||
|
||||
/// <summary>
|
||||
/// UI切换中间件处理器接口,支持包裹整个变更过程的逻辑。
|
||||
/// Around 处理器在变更前后都会执行,可以控制是否继续执行变更。
|
||||
/// 适用于:性能监控、事务管理、权限验证、日志记录等横切关注点。
|
||||
/// </summary>
|
||||
public interface IUiAroundTransitionHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// 处理器优先级,数值越小越先执行(外层)。
|
||||
/// 建议范围:-1000 到 1000。
|
||||
/// </summary>
|
||||
int Priority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否应该处理当前事件。
|
||||
/// </summary>
|
||||
/// <param name="event">UI切换事件。</param>
|
||||
/// <returns>如果应该处理则返回 true,否则返回 false。</returns>
|
||||
bool ShouldHandle(UiTransitionEvent @event);
|
||||
|
||||
/// <summary>
|
||||
/// 执行中间件逻辑。
|
||||
/// </summary>
|
||||
/// <param name="event">UI切换事件。</param>
|
||||
/// <param name="next">下一个中间件或实际操作的委托。调用此委托以继续执行流程。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>异步任务。</returns>
|
||||
Task HandleAsync(
|
||||
UiTransitionEvent @event,
|
||||
Func<Task> next,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
@ -92,10 +92,13 @@ public abstract class SceneRouterBase
|
||||
|
||||
var @event = CreateEvent(sceneKey, SceneTransitionType.Replace, param);
|
||||
|
||||
await BeforeChangeAsync(@event);
|
||||
await ClearInternalAsync();
|
||||
await PushInternalAsync(sceneKey, param);
|
||||
AfterChange(@event);
|
||||
await _pipeline.ExecuteAroundAsync(@event, async () =>
|
||||
{
|
||||
await BeforeChangeAsync(@event);
|
||||
await ClearInternalAsync();
|
||||
await PushInternalAsync(sceneKey, param);
|
||||
AfterChange(@event);
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -209,9 +212,12 @@ public abstract class SceneRouterBase
|
||||
|
||||
var @event = CreateEvent(sceneKey, SceneTransitionType.Push, param);
|
||||
|
||||
await BeforeChangeAsync(@event);
|
||||
await PushInternalAsync(sceneKey, param);
|
||||
AfterChange(@event);
|
||||
await _pipeline.ExecuteAroundAsync(@event, async () =>
|
||||
{
|
||||
await BeforeChangeAsync(@event);
|
||||
await PushInternalAsync(sceneKey, param);
|
||||
AfterChange(@event);
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -287,9 +293,12 @@ public abstract class SceneRouterBase
|
||||
|
||||
var @event = CreateEvent(null, SceneTransitionType.Pop);
|
||||
|
||||
await BeforeChangeAsync(@event);
|
||||
await PopInternalAsync();
|
||||
AfterChange(@event);
|
||||
await _pipeline.ExecuteAroundAsync(@event, async () =>
|
||||
{
|
||||
await BeforeChangeAsync(@event);
|
||||
await PopInternalAsync();
|
||||
AfterChange(@event);
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -355,9 +364,12 @@ public abstract class SceneRouterBase
|
||||
|
||||
var @event = CreateEvent(null, SceneTransitionType.Clear);
|
||||
|
||||
await BeforeChangeAsync(@event);
|
||||
await ClearInternalAsync();
|
||||
AfterChange(@event);
|
||||
await _pipeline.ExecuteAroundAsync(@event, async () =>
|
||||
{
|
||||
await BeforeChangeAsync(@event);
|
||||
await ClearInternalAsync();
|
||||
AfterChange(@event);
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@ -25,6 +25,8 @@ namespace GFramework.Game.scene;
|
||||
public class SceneTransitionPipeline
|
||||
{
|
||||
private static readonly ILogger Log = LoggerFactoryResolver.Provider.CreateLogger(nameof(SceneTransitionPipeline));
|
||||
private readonly List<ISceneAroundTransitionHandler> _aroundHandlers = [];
|
||||
private readonly Dictionary<ISceneAroundTransitionHandler, SceneTransitionHandlerOptions> _aroundOptions = new();
|
||||
private readonly List<ISceneTransitionHandler> _handlers = [];
|
||||
private readonly Dictionary<ISceneTransitionHandler, SceneTransitionHandlerOptions> _options = new();
|
||||
|
||||
@ -67,6 +69,44 @@ public class SceneTransitionPipeline
|
||||
Log.Debug("Handler unregistered: {0}", handler.GetType().Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册 Around 中间件处理器。
|
||||
/// </summary>
|
||||
/// <param name="handler">处理器实例。</param>
|
||||
/// <param name="options">执行选项,如果为 null 则使用默认选项。</param>
|
||||
public void RegisterAroundHandler(ISceneAroundTransitionHandler handler,
|
||||
SceneTransitionHandlerOptions? options = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(handler);
|
||||
|
||||
if (_aroundHandlers.Contains(handler))
|
||||
{
|
||||
Log.Debug("Around handler already registered: {0}", handler.GetType().Name);
|
||||
return;
|
||||
}
|
||||
|
||||
_aroundHandlers.Add(handler);
|
||||
_aroundOptions[handler] = options ?? new SceneTransitionHandlerOptions();
|
||||
Log.Debug(
|
||||
"Around handler registered: {0}, Priority={1}",
|
||||
handler.GetType().Name,
|
||||
handler.Priority
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注销 Around 中间件处理器。
|
||||
/// </summary>
|
||||
/// <param name="handler">处理器实例。</param>
|
||||
public void UnregisterAroundHandler(ISceneAroundTransitionHandler handler)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(handler);
|
||||
|
||||
if (!_aroundHandlers.Remove(handler)) return;
|
||||
_aroundOptions.Remove(handler);
|
||||
Log.Debug("Around handler unregistered: {0}", handler.GetType().Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行指定阶段的所有处理器。
|
||||
/// </summary>
|
||||
@ -114,6 +154,53 @@ public class SceneTransitionPipeline
|
||||
Log.Debug("Pipeline execution completed for phases: {0}", phases);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行 Around 中间件处理器,包裹核心操作。
|
||||
/// </summary>
|
||||
/// <param name="event">场景过渡事件。</param>
|
||||
/// <param name="coreAction">核心操作委托。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>异步任务。</returns>
|
||||
public async Task ExecuteAroundAsync(
|
||||
SceneTransitionEvent @event,
|
||||
Func<Task> coreAction,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var handlers = _aroundHandlers
|
||||
.Where(h => h.ShouldHandle(@event))
|
||||
.OrderBy(h => h.Priority)
|
||||
.ToList();
|
||||
|
||||
if (handlers.Count == 0)
|
||||
{
|
||||
await coreAction();
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Debug(
|
||||
"Executing {0} around handlers for event: {1}",
|
||||
handlers.Count,
|
||||
@event.TransitionType
|
||||
);
|
||||
|
||||
// 构建中间件链
|
||||
Func<Task> pipeline = coreAction;
|
||||
for (int i = handlers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var handler = handlers[i];
|
||||
var options = _aroundOptions[handler];
|
||||
var next = pipeline;
|
||||
|
||||
pipeline = async () =>
|
||||
{
|
||||
await ExecuteSingleAroundHandlerAsync(
|
||||
handler, options, @event, next, cancellationToken);
|
||||
};
|
||||
}
|
||||
|
||||
await pipeline();
|
||||
}
|
||||
|
||||
private List<ISceneTransitionHandler> FilterAndSortHandlers(
|
||||
SceneTransitionEvent @event,
|
||||
SceneTransitionPhases phases)
|
||||
@ -179,4 +266,37 @@ public class SceneTransitionPipeline
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ExecuteSingleAroundHandlerAsync(
|
||||
ISceneAroundTransitionHandler handler,
|
||||
SceneTransitionHandlerOptions options,
|
||||
SceneTransitionEvent @event,
|
||||
Func<Task> next,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
Log.Debug("Executing around handler: {0}", handler.GetType().Name);
|
||||
|
||||
try
|
||||
{
|
||||
using var timeoutCts = options.TimeoutMs > 0
|
||||
? new CancellationTokenSource(options.TimeoutMs)
|
||||
: null;
|
||||
|
||||
using var linkedCts = timeoutCts != null && cancellationToken.CanBeCanceled
|
||||
? CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token)
|
||||
: null;
|
||||
|
||||
await handler.HandleAsync(@event, next, linkedCts?.Token ?? cancellationToken);
|
||||
|
||||
Log.Debug("Around handler completed: {0}", handler.GetType().Name);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("Around handler failed: {0}, Error: {1}",
|
||||
handler.GetType().Name, ex.Message);
|
||||
|
||||
if (!options.ContinueOnError)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -89,9 +89,12 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
|
||||
var @event = CreateEvent(uiKey, UiTransitionType.Push, policy, param);
|
||||
Log.Debug("Push UI Page: key={0}, policy={1}, stackBefore={2}", uiKey, policy, _stack.Count);
|
||||
|
||||
BeforeChange(@event);
|
||||
DoPushPageInternal(uiKey, param, policy);
|
||||
AfterChange(@event);
|
||||
_pipeline.ExecuteAroundAsync(@event, async () =>
|
||||
{
|
||||
BeforeChange(@event);
|
||||
DoPushPageInternal(uiKey, param, policy);
|
||||
AfterChange(@event);
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -111,9 +114,12 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
|
||||
var @event = CreateEvent(uiKey, UiTransitionType.Push, policy, param);
|
||||
Log.Debug("Push existing UI Page: key={0}, policy={1}, stackBefore={2}", uiKey, policy, _stack.Count);
|
||||
|
||||
BeforeChange(@event);
|
||||
DoPushPageInternal(page, param, policy);
|
||||
AfterChange(@event);
|
||||
_pipeline.ExecuteAroundAsync(@event, async () =>
|
||||
{
|
||||
BeforeChange(@event);
|
||||
DoPushPageInternal(page, param, policy);
|
||||
AfterChange(@event);
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -138,9 +144,12 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
|
||||
var nextUiKey = _stack.Count > 1 ? _stack.ElementAt(1).Key : null;
|
||||
var @event = CreateEvent(nextUiKey, UiTransitionType.Pop);
|
||||
|
||||
BeforeChange(@event);
|
||||
DoPopInternal(policy);
|
||||
AfterChange(@event);
|
||||
_pipeline.ExecuteAroundAsync(@event, async () =>
|
||||
{
|
||||
BeforeChange(@event);
|
||||
DoPopInternal(policy);
|
||||
AfterChange(@event);
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -153,14 +162,17 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
|
||||
var @event = CreateEvent(uiKey, UiTransitionType.Replace, pushPolicy, param);
|
||||
Log.Debug("Replace UI Stack with page: key={0}, popPolicy={1}, pushPolicy={2}", uiKey, popPolicy, pushPolicy);
|
||||
|
||||
BeforeChange(@event);
|
||||
DoClearInternal(popPolicy);
|
||||
_pipeline.ExecuteAroundAsync(@event, async () =>
|
||||
{
|
||||
BeforeChange(@event);
|
||||
DoClearInternal(popPolicy);
|
||||
|
||||
var page = _factory.Create(uiKey);
|
||||
Log.Debug("Get/Create UI Page instance for Replace: {0}", page.GetType().Name);
|
||||
var page = _factory.Create(uiKey);
|
||||
Log.Debug("Get/Create UI Page instance for Replace: {0}", page.GetType().Name);
|
||||
|
||||
DoPushPageInternal(page, param, pushPolicy);
|
||||
AfterChange(@event);
|
||||
DoPushPageInternal(page, param, pushPolicy);
|
||||
AfterChange(@event);
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -175,11 +187,14 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
|
||||
Log.Debug("Replace UI Stack with existing page: key={0}, popPolicy={1}, pushPolicy={2}",
|
||||
uiKey, popPolicy, pushPolicy);
|
||||
|
||||
BeforeChange(@event);
|
||||
DoClearInternal(popPolicy);
|
||||
Log.Debug("Use existing UI Page instance for Replace: {0}", page.GetType().Name);
|
||||
DoPushPageInternal(page, param, pushPolicy);
|
||||
AfterChange(@event);
|
||||
_pipeline.ExecuteAroundAsync(@event, async () =>
|
||||
{
|
||||
BeforeChange(@event);
|
||||
DoClearInternal(popPolicy);
|
||||
Log.Debug("Use existing UI Page instance for Replace: {0}", page.GetType().Name);
|
||||
DoPushPageInternal(page, param, pushPolicy);
|
||||
AfterChange(@event);
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -190,9 +205,12 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
|
||||
var @event = CreateEvent(string.Empty, UiTransitionType.Clear);
|
||||
Log.Debug("Clear UI Stack, stackCount={0}", _stack.Count);
|
||||
|
||||
BeforeChange(@event);
|
||||
DoClearInternal(UiPopPolicy.Destroy);
|
||||
AfterChange(@event);
|
||||
_pipeline.ExecuteAroundAsync(@event, async () =>
|
||||
{
|
||||
BeforeChange(@event);
|
||||
DoClearInternal(UiPopPolicy.Destroy);
|
||||
AfterChange(@event);
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -11,6 +11,8 @@ namespace GFramework.Game.ui;
|
||||
public class UiTransitionPipeline
|
||||
{
|
||||
private static readonly ILogger Log = LoggerFactoryResolver.Provider.CreateLogger("UiTransitionPipeline");
|
||||
private readonly List<IUiAroundTransitionHandler> _aroundHandlers = [];
|
||||
private readonly Dictionary<IUiAroundTransitionHandler, UiTransitionHandlerOptions> _aroundOptions = new();
|
||||
private readonly List<IUiTransitionHandler> _handlers = [];
|
||||
private readonly Dictionary<IUiTransitionHandler, UiTransitionHandlerOptions> _options = new();
|
||||
|
||||
@ -53,6 +55,43 @@ public class UiTransitionPipeline
|
||||
Log.Debug("Handler unregistered: {0}", handler.GetType().Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册 Around 中间件处理器
|
||||
/// </summary>
|
||||
/// <param name="handler">处理器实例</param>
|
||||
/// <param name="options">执行选项</param>
|
||||
public void RegisterAroundHandler(IUiAroundTransitionHandler handler, UiTransitionHandlerOptions? options = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(handler);
|
||||
|
||||
if (_aroundHandlers.Contains(handler))
|
||||
{
|
||||
Log.Debug("Around handler already registered: {0}", handler.GetType().Name);
|
||||
return;
|
||||
}
|
||||
|
||||
_aroundHandlers.Add(handler);
|
||||
_aroundOptions[handler] = options ?? new UiTransitionHandlerOptions();
|
||||
Log.Debug(
|
||||
"Around handler registered: {0}, Priority={1}",
|
||||
handler.GetType().Name,
|
||||
handler.Priority
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注销 Around 中间件处理器
|
||||
/// </summary>
|
||||
/// <param name="handler">处理器实例</param>
|
||||
public void UnregisterAroundHandler(IUiAroundTransitionHandler handler)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(handler);
|
||||
|
||||
if (!_aroundHandlers.Remove(handler)) return;
|
||||
_aroundOptions.Remove(handler);
|
||||
Log.Debug("Around handler unregistered: {0}", handler.GetType().Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行指定阶段的所有Handler
|
||||
/// </summary>
|
||||
@ -100,6 +139,53 @@ public class UiTransitionPipeline
|
||||
Log.Debug("Pipeline execution completed for phases: {0}", phases);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行 Around 中间件处理器,包裹核心操作
|
||||
/// </summary>
|
||||
/// <param name="event">UI切换事件</param>
|
||||
/// <param name="coreAction">核心操作委托</param>
|
||||
/// <param name="cancellationToken">取消令牌</param>
|
||||
/// <returns>异步任务</returns>
|
||||
public async Task ExecuteAroundAsync(
|
||||
UiTransitionEvent @event,
|
||||
Func<Task> coreAction,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var handlers = _aroundHandlers
|
||||
.Where(h => h.ShouldHandle(@event))
|
||||
.OrderBy(h => h.Priority)
|
||||
.ToList();
|
||||
|
||||
if (handlers.Count == 0)
|
||||
{
|
||||
await coreAction();
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Debug(
|
||||
"Executing {0} around handlers for event: {1}",
|
||||
handlers.Count,
|
||||
@event.TransitionType
|
||||
);
|
||||
|
||||
// 构建中间件链
|
||||
Func<Task> pipeline = coreAction;
|
||||
for (int i = handlers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var handler = handlers[i];
|
||||
var options = _aroundOptions[handler];
|
||||
var next = pipeline;
|
||||
|
||||
pipeline = async () =>
|
||||
{
|
||||
await ExecuteSingleAroundHandlerAsync(
|
||||
handler, options, @event, next, cancellationToken);
|
||||
};
|
||||
}
|
||||
|
||||
await pipeline();
|
||||
}
|
||||
|
||||
private List<IUiTransitionHandler> FilterAndSortHandlers(
|
||||
UiTransitionEvent @event,
|
||||
UiTransitionPhases phases)
|
||||
@ -165,4 +251,37 @@ public class UiTransitionPipeline
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ExecuteSingleAroundHandlerAsync(
|
||||
IUiAroundTransitionHandler handler,
|
||||
UiTransitionHandlerOptions options,
|
||||
UiTransitionEvent @event,
|
||||
Func<Task> next,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
Log.Debug("Executing around handler: {0}", handler.GetType().Name);
|
||||
|
||||
try
|
||||
{
|
||||
using var timeoutCts = options.TimeoutMs > 0
|
||||
? new CancellationTokenSource(options.TimeoutMs)
|
||||
: null;
|
||||
|
||||
using var linkedCts = timeoutCts != null && cancellationToken.CanBeCanceled
|
||||
? CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token)
|
||||
: null;
|
||||
|
||||
await handler.HandleAsync(@event, next, linkedCts?.Token ?? cancellationToken);
|
||||
|
||||
Log.Debug("Around handler completed: {0}", handler.GetType().Name);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("Around handler failed: {0}, Error: {1}",
|
||||
handler.GetType().Name, ex.Message);
|
||||
|
||||
if (!options.ContinueOnError)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user