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

创建简单异步工作流活动:AsyncCodeActivity异步OData请求实现求助

帮你搞定Workflow Foundation异步OData请求活动的实现

刚入门WF踩异步活动的坑太正常了,尤其是结合HttpClient的时候,稍不注意就会出资源泄漏或者异步上下文不兼容的问题。我来一步步帮你把这个OData请求的异步活动捋顺!

首先纠正几个核心问题

  • 直接在活动里new HttpClient会导致socket资源耗尽,HttpClient是线程安全的,应该用静态单例或者依赖注入HttpClientFactory来管理
  • AsyncCodeActivity需要遵循WF的异步编程模型(APM),不能直接用普通的async/await,得把Task包装成WF能识别的IAsyncResult
  • IDisposable的实现要区分静态资源和实例资源,避免错误释放

修正后的OData活动基类

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Activities;

public abstract class ODataActivity<TResult> : AsyncCodeActivity<TResult>, IDisposable
{
    // 用静态HttpClient单例,避免重复创建导致的资源泄漏
    private static readonly HttpClient _apiClient = new HttpClient(new HttpClientHandler())
    {
        BaseAddress = new Uri("替换成你的OData服务基础地址")
    };

    protected ODataActivity()
    {
        // 预配置OData请求头,通用配置可以放这里
        _apiClient.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json"));
        _apiClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
    }

    // 重写WF的BeginExecute,启动异步任务
    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        // 调用子类实现的OData请求逻辑
        var requestTask = ExecuteODataRequestAsync(context);
        // 把Task包装成WF兼容的IAsyncResult
        var asyncResult = new TaskAsyncResult<TResult>(requestTask, callback, state);
        context.UserState = asyncResult;
        return asyncResult;
    }

    // 重写WF的EndExecute,获取异步结果
    protected override TResult EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        var asyncResult = (TaskAsyncResult<TResult>)result;
        // 等待任务完成,异常会在这里抛出,WF会自动处理错误流程
        return asyncResult.Task.GetAwaiter().GetResult();
    }

    // 抽象方法,让子类实现具体的OData请求逻辑
    protected abstract Task<TResult> ExecuteODataRequestAsync(AsyncCodeActivityContext context);

    #region IDisposable实现
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // 注意:静态HttpClient不需要在这里释放,它是应用级单例
            // 如果是用HttpClientFactory创建的实例,也不需要手动释放,工厂会管理
            // _apiClient?.Dispose();
        }
    }
    #endregion
}

// 辅助类:把Task包装成WF需要的IAsyncResult
public class TaskAsyncResult<TResult> : IAsyncResult
{
    private readonly Task<TResult> _task;

    public TaskAsyncResult(Task<TResult> task, AsyncCallback callback, object state)
    {
        _task = task;
        AsyncState = state;
        // 任务完成后触发WF的回调
        if (callback != null)
        {
            _task.ContinueWith(t => callback(this), TaskScheduler.Default);
        }
    }

    public object AsyncState { get; }
    public WaitHandle AsyncWaitHandle => ((IAsyncResult)_task).AsyncWaitHandle;
    public bool CompletedSynchronously => ((IAsyncResult)_task).CompletedSynchronously;
    public bool IsCompleted => ((IAsyncResult)_task).IsCompleted;
    public Task<TResult> Task => _task;
}

子类示例:获取单个OData实体

比如你要实现一个根据ID获取OData实体的活动,可以这么写:

using System.Activities;
using System.Net.Http.Json;

public class GetODataEntityActivity : ODataActivity<MyEntity>
{
    // 定义WF的输入参数:实体ID
    public InArgument<int> EntityId { get; set; }

    protected override async Task<MyEntity> ExecuteODataRequestAsync(AsyncCodeActivityContext context)
    {
        // 从WF上下文获取输入参数
        var entityId = EntityId.Get(context);
        // 构造OData请求路径
        var requestPath = $"odata/MyEntities({entityId})";

        // 发起异步GET请求
        var response = await _apiClient.GetAsync(requestPath);
        // 确保请求成功,失败会抛出HttpRequestException
        response.EnsureSuccessStatusCode();

        // 反序列化响应为实体对象
        return await response.Content.ReadFromJsonAsync<MyEntity>();
    }
}

// 示例实体类,替换成你的OData实体
public class MyEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    // 其他属性...
}

几个关键注意事项

  • HttpClient管理:如果需要多个不同配置的HttpClient,推荐用IHttpClientFactory注入,而不是创建多个静态实例
  • WF上下文使用:在ExecuteODataRequestAsync中,只能在方法执行期间使用AsyncCodeActivityContext,不能在异步回调里用(比如await之后),因为上下文可能已经被WF回收
  • 异常处理:如果需要自定义错误处理,可以在EndExecute中捕获异常,或者在工作流里给活动添加FaultHandler
  • 动态配置:如果要动态设置BaseAddress或者请求头,可以把这些做成InArgument,在ExecuteODataRequestAsync里从上下文获取

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 02:30:10