创建简单异步工作流活动: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
相关产品推荐
相关产品推荐

