You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何重构过度传递的操作上下文(context)实例?

重构冗余的Action上下文传递方案

这种显式传递Context实例导致代码冗余的问题我之前也碰到过,结合你的场景——需要在日志等场景获取ActionBlockName,且同一个Action可能属于不同Block——给你几个实用的重构方案:

1. 使用线程/异步本地存储(AsyncLocal)

如果你的应用涉及多线程或异步操作,用AsyncLocal(以.NET为例)可以让上下文在当前执行流中自动传递,不用手动传参:

// 定义一个上下文持有者类
public static class ActionContextHolder
{
    private static readonly AsyncLocal<ActionContext> _currentContext = new AsyncLocal<ActionContext>();

    // 公开当前上下文
    public static ActionContext Current => _currentContext.Value;

    // 设置上下文并返回可释放对象,用于恢复之前的上下文
    public static IDisposable SetContext(ActionContext context)
    {
        var previousContext = _currentContext.Value;
        _currentContext.Value = context;
        return new Disposable(() => _currentContext.Value = previousContext);
    }

    // 内部辅助的可释放类
    private class Disposable : IDisposable
    {
        private readonly Action _disposeAction;
        public Disposable(Action disposeAction) => _disposeAction = disposeAction;
        public void Dispose() => _disposeAction();
    }
}

使用方式非常简洁,用using块包裹执行逻辑,确保上下文在执行完成后恢复:

using (ActionContextHolder.SetContext(context))
{
    ExecuteAction(action); // 这里不用再传context了!
}

然后在ExecuteAction内部直接获取上下文:

public void ExecuteAction(Action action)
{
    var logString = $"action {action.Name} was executed as part of {ActionContextHolder.Current.ActionBlockName} action block";
    // 执行action逻辑...
}

2. 依赖注入(DI)注入上下文

如果你的应用已经使用了依赖注入容器,可以把ActionContext注册为**作用域(Scoped)**服务,这样在需要的地方自动注入,避免手动传递:

注册上下文服务

// 以.NET的DI为例,注册为Scoped,确保每个请求/作用域内上下文唯一
services.AddScoped<ActionContext>(sp => 
{
    // 根据当前场景创建或获取上下文实例
    return new ActionContext { ActionBlockName = "YourBlockName" };
});

在执行器中注入上下文

public class ActionExecutor
{
    private readonly ActionContext _context;

    // 通过构造函数注入上下文
    public ActionExecutor(ActionContext context)
    {
        _context = context;
    }

    public void ExecuteAction(Action action)
    {
        var logString = $"action {action.Name} was executed as part of {_context.ActionBlockName} action block";
        // 执行action逻辑...
    }
}

使用时直接从DI容器获取ActionExecutor实例调用方法即可,不用手动传Context。

3. 将上下文与Action绑定(固定场景适用)

如果同一个Action实例始终属于同一个ActionBlock,可以在初始化Action时直接把上下文绑定到Action的属性上:

public class Action
{
    public string Name { get; set; }
    // 添加上下文属性
    public ActionContext Context { get; set; }
}

然后ExecuteAction只需要接收Action:

public void ExecuteAction(Action action)
{
    var logString = $"action {action.Name} was executed as part of {action.Context.ActionBlockName} action block";
    // 执行action逻辑...
}

⚠️ 注意:这个方案只适用于Action和ActionBlock是固定绑定的场景,如果同一个Action实例可能被用于不同的ActionBlock,就不要用这个方法。

4. 装饰器模式包装Action

用装饰器模式给Action添加上下文相关的逻辑(比如日志),同时保持Action本身的职责单一:

定义Action接口

public interface IAction
{
    string Name { get; }
    void Execute();
}

// 实现具体的Action
public class ConcreteAction : IAction
{
    public string Name { get; set; }

    public void Execute()
    {
        // 原始的Action执行逻辑
    }
}

实现上下文装饰器

public class ActionWithContextDecorator : IAction
{
    private readonly IAction _innerAction;
    private readonly ActionContext _context;

    public ActionWithContextDecorator(IAction innerAction, ActionContext context)
    {
        _innerAction = innerAction;
        _context = context;
    }

    public string Name => _innerAction.Name;

    public void Execute()
    {
        // 先处理日志逻辑
        var logString = $"action {_innerAction.Name} was executed as part of {_context.ActionBlockName} action block";
        // 执行原始Action逻辑
        _innerAction.Execute();
    }
}

使用时先包装Action,之后直接调用装饰后的实例:

var decoratedAction = new ActionWithContextDecorator(action, context);
decoratedAction.Execute();

方案选择建议

  • 异步/多线程场景 → 优先选AsyncLocal
  • 已使用DI容器 → 优先选依赖注入
  • Action与Block固定绑定 → 选上下文绑定属性
  • 想保持单一职责、扩展灵活 → 选装饰器模式

内容的提问来源于stack exchange,提问作者user2363676

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:55:13