未处理System.InvalidOperationException
  HResult=-2146233079
  Message=AsyncCodeActivity.BeginExecute 必须返回 IAsyncResult,且其 IAsyncResult.AsyncState 必须为运行时提供的状态对象。
  Source=System.Activities
  StackTrace:
       在 System.Activities.WorkflowApplication.Invoke(Activity activity, IDictionary`2 inputs, WorkflowInstanceExtensionManager extensions, TimeSpan timeout)
       在 System.Activities.WorkflowInvoker.Invoke(Activity workflow, TimeSpan timeout, WorkflowInstanceExtensionManager extensions)
       在 System.Activities.WorkflowInvoker.Invoke(Activity workflow)
       在 AsyncCodeActivitySample.Program.Main(String[] args) 位置 c:下载AsyncCodeActivitySampleAsyncCodeActivitySampleProgram.cs:行号 13
  InnerException:

SNAGHTML5cf08b9

异常信息如上文,此问题主要出在实现AsyncCodeActivity的抽象方法BeginExecute时返回的IAsyncResult对象未能正确设定AsyncState属性上。

通常我们返回的是一个普通的Task<T>对象,未进行异步编程的特殊处理,

这篇老外的博客提供了解决办法,原话:

In my previous blog post Invoke Workflow (WF) using Task-based Asynchronous Pattern (TAP) in ASP.NET MVC, I mentioned that I've created a CodeActivity namely GetDeal. This is actually an AsyncCodeActivity which make the http request using the new HttpClient. This is a little tricky because  AsyncCodeActivity is based on the APM pattern (Asynchronous Programming Model pattern aka IAsyncResult pattern aka Begin/End pattern) and the new HttpClient calls are based on TAP pattern (Task-based Asynchronous Pattern). In order to resolve this, I've essentially followed the approach mentioned on msdn here and created an extension method for Task<TResult>.

Within the BeginExecute, I call this method on the Task object returned by HttpClient.GetAsync(string) and then within the EndExecute, I read the HttpResponseMessage from the Task object.

[Note: Since we need the HttpClient instance till the task duration, I've defined the disposing of it within the Task.ContinueWith]

说实话我英文不好,也就没细看这片英文,暂时不做深入探讨,这里只是简单贴出自己测试通过代码。

首先需要这样一个扩展方法:

    /// <summary>
    /// 转换为异步编程模型(Asynchronous Programming Model),用于WF的AsyncCodeActivity中的BeginExecute方法中使用,如果不进行此转换,通常就会因不包含AsyncState属性而引发InvalidOperationException
    /// 代码源自:http://tweetycodingxp.blogspot.jp/2013/06/using-task-based-asynchronous-pattern.html
    /// </summary>
    /// <typeparam name="TResult">返回值类型</typeparam>
    /// <param name="task">原Task对象</param>
    /// <param name="callback">BeginExecute方法中的callback参数</param>
    /// <param name="state">BeginExecute方法中的state参数</param>
    /// <returns>异步编程模型形式的Task</returns>
    public static Task<TResult> ToApm<TResult>(this Task<TResult> task, AsyncCallback callback, object state)
    {
        if (task.AsyncState == state)
        {
            if (callback != null)
            {
                task.ContinueWith(delegate { callback(task); },
                                  CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default);
            }
            return task;
        }

        var tcs = new TaskCompletionSource<TResult>(state);
        task.ContinueWith(obj =>
        {
            if (task.IsFaulted) tcs.TrySetException(task.Exception.InnerExceptions);
            else if (task.IsCanceled) tcs.TrySetCanceled();
            else tcs.TrySetResult(task.Result);

            if (callback != null) callback(tcs.Task);
        }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default);
        return tcs.Task;
    }

然后是应用代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Activities;
using System.Threading.Tasks;

namespace Core.WFActivity
{
    [Designer(typeof(网址输入))]
    public sealed class 发起HttpGet请求 : AsyncCodeActivity
    {
        /// <summary>
        /// 目标网址
        /// </summary>
        [RequiredArgument]
        public InArgument<String> 网址 { get; set; }

        [RequiredArgument]
        public OutArgument<HttpResponseMessage> 响应消息 { get; set; }


        protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
        {
            var hc = new HttpClient();
            var task = hc
                .GetAsync(context.GetValue(网址))
                .ToApm(callback, state);
            task.ContinueWith(q =>
                    hc.Dispose()
                );
            return task;
        }

        protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
        {
            context.SetValue(响应消息, (result as Task<HttpResponseMessage>).Result);
        }
    }
}

注意高亮部分,一定要转换成Apm,并且返回的也一定要是Apm,千万不要在后面把ContinueWith返回的结果给返回出去。

另外顺手转来老外的代码,一起学习一下:

namespace Activities.SmartPea
{
    public sealed class GetDeal : AsyncCodeActivity
    {
        public InArgument<string> ItemName { get; set; }

        public InArgument<string> Zip { get; set; }

        public OutArgument<IEnumerable<Deal>> Deals { get; set; }

        protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback,
                                                     object state)
        {
            var httpClient = new HttpClient();
            var task =
                httpClient.GetAsync(string.Format("http://api.smartpea.com/api/deal/?title={0}&zip={1}",
                                                  ItemName.Get(context), Zip.Get(context)))
                          .ToApm(callback, state);
            task.ContinueWith(obj => httpClient.Dispose());
            return task;
        }

        protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
        {
            HttpResponseMessage response = ((Task<HttpResponseMessage>)result).Result;
            IEnumerable<Deal> deals = new List<Deal>();
            if (response.IsSuccessStatusCode &&
                string.Compare(response.Content.Headers.ContentType.MediaType, "application/json",
                               StringComparison.OrdinalIgnoreCase) == 0)
                deals = response.Content.ReadAsAsync<IEnumerable<Deal>>().Result; //.ToObject<IEnumerable<Deal>>();
            Deals.Set(context,deals);
        }
    }

    public static class Extensions
    {
        public static Task<TResult> ToApm<TResult>(this Task<TResult> task, AsyncCallback callback, object state)
        {
            if (task.AsyncState == state)
            {
                if (callback != null)
                {
                    task.ContinueWith(delegate { callback(task); },
                                      CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default);
                }
                return task;
            }

            var tcs = new TaskCompletionSource<TResult>(state);
            task.ContinueWith(obj =>
                                  {
                                      if (task.IsFaulted) tcs.TrySetException(task.Exception.InnerExceptions);
                                      else if (task.IsCanceled) tcs.TrySetCanceled();
                                      else tcs.TrySetResult(task.Result);

                                      if (callback != null) callback(tcs.Task);
                                  }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default);
            return tcs.Task;
        }
    }
}
转载此文章时须注明转载自”SkyD(斯克迪亚)开发者博客“,并保留此文章的Url链接

作者信息

昵称
斯克迪亚

查看其所发布的所有文章

总积分
2420
注册时间
(2018年5月4日 19:06)

评论

目前还没有任何评论。

[切换到移动版页面]