0

I have the below code which calls a workflow. I am wondering if I can changed the controller to be asynchronous using the async ctp.

public ActionResult Index()
    {
        var input = new Dictionary<string, object>();
        input["ViewData"] = this.ViewData;
        var userState = "BeginInvoke example";

        var invoker = new WorkflowInvoker(HelloMvcDefinition);

        Task workflowTask = Task.Factory.FromAsync<IDictionary<string, object>>(
            invoker.BeginInvoke(input, WorkflowCompletedCallback, userState),
            invoker.EndInvoke);

        workflowTask.Wait();

        return View();
    }

I have tried this but I cannot seem to get it to work:

public async Task<ActionResult> Index()
    {
        var input = new Dictionary<string, object>();
        input["ViewData"] = this.ViewData;
        var userState = "BeginInvoke example";

        var invoker = new WorkflowInvoker(HelloMvcDefinition);

        Task workflowTask = Task.Factory.FromAsync<IDictionary<string, object>>(
            invoker.BeginInvoke(input, WorkflowCompletedCallback, userState),
            invoker.EndInvoke);

        await workflowTask;

        return View();
    }

Unfortunately the view does not seem to work. Any ideas on what I am doing wrong?

EDIT After taking advice I have changed the method to this

public class HelloController : AsyncController
{
    private static readonly HelloWorkflow HelloMvcDefinition = new HelloWorkflow();

    public Task<ViewResult> Index()
    {
        var input = new Dictionary<string, object>();
        input["ViewData"] = ViewData;
        const string userState = "BeginInvoke example";

        var invoker = new WorkflowInvoker(HelloMvcDefinition);

        return Task.Factory.FromAsync<IDictionary<string, object>>(
            invoker.BeginInvoke(input, WorkflowCompletedCallback, userState),
            invoker.EndInvoke).ContinueWith(t => View());
    }       

    static void WorkflowCompletedCallback(IAsyncResult result)
    {

    }
}

Which works fine so the problem must be how I am using the async keyword.

Thanks

tereško
  • 58,060
  • 25
  • 98
  • 150
Beats
  • 201
  • 3
  • 13
  • Can you explain the symptom more? Also, if you just change the await to .Wait() (leaving it marked async) does it start working again? Thanks – James Manning Apr 02 '12 at 15:30
  • The problem seems to stem from the using async keyword. If I add the Wait() it still does not work. When I invoke this controller, the view should appear with the message being shown from the workflow but currently the browser just keeps spinning. – Beats Apr 03 '12 at 11:34

2 Answers2

1

We can use the TAP pattern for invoking windows workflow as follows - (Details are mentioned in my blog post http://tweetycodingxp.blogspot.com/2013/06/invoke-workflow-wf-using-task-based.html)

public async Task<ActionResult> Index(string id)
{
    var wfInputArgs = new Dictionary<string, object>
    {
        ...
    };

    var wfOutputArgs = await Task<IDictionary<string, object>>.Factory.StartNew(
        () => WorkflowInvoker.Invoke(new MyWorkflow(), wfInputArgs));
    var result = wfOutputArgs["Output1"] as IEnumerable<Class1>;
    ...
    return View(model);
}
David Yaw
  • 27,383
  • 4
  • 60
  • 93
0

Derive from AsyncController instead of Controller.

EDIT: You may also be experiencing a known bug where ASP.NET MVC4 will hang if the action returns a completed Task. You can work around this bug by adding await Task.Yield(); at the top of the action method.

On an unrelated note, this code is more efficient (and shorter, too):

var workflowTask = Task.Factory.FromAsync(invoker.BeginInvoke, invoker.EndInvoke,
    input, userState);
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810