开发者

Multilevel asynchronous method call pattern in c#

开发者 https://www.devze.com 2023-01-03 11:43 出处:网络
I have design problem regarding async calls to method. I\'d like to know best/good pattern to call async method, which calls another async method, which calls another async method :)

I have design problem regarding async calls to method.

I'd like to know best/good pattern to call async method, which calls another async method, which calls another async method :)

In other words, I have WCF service reference created with async methods and I want to call them from another async method which is called by other async method.开发者_如何学编程 All this for non blocking GUI.

Thanks!


If your only goal is to have a non blocking GUI then the multiple levels are unnecessary. Once your toplevel method runs in the background, your GUI is freed. Using multiple levels does bring extra complexity.

There can be other reasons (performance) to call the lower level methods async, but it depends. Do you need to wait for results later on?

So I don't think there is a 'pattern' here.


One example of when it would be good to have multiple levels would be when creating asynchronous controllers in a MVC framework like MonoRail or MS MVC. The end thing that calls into something IO-blocking, such as SqlCommand from System.Data.SqlClient or some socket, would put the IO operand on an IO completion port: http://msdn.microsoft.com/library/aa365198, which would save the managed/unmanaged thread's quanta for something more useful.

If you write classes that return IAsyncResult, then you are not far away from implementing co-routines. Here's a good article about how asynchronous programming can be used with coroutines: http://blogs.msdn.com/b/pfxteam/archive/2009/06/30/9809774.aspx.

Caliburn, a WPF framework supports co-routines natively. The task parallel library, released with .Net 4, has given its task the IAsyncResult interface. [If you are at 3.5 then you might need to create your own implementation (they are pretty simple to make, just implement the interface).] Co-routines are a way of using the compiler re-writes of IEnumerable to push IAsyncResults to a stack of things to do (as seen from the "async manager").

F# async (like seen in the down-voted answer) uses a monad (as monadic as they get on the CLR) to move the state of the asynchronous request from the Begin* to End* methods. The compilers both this into nested lambda expressions/SelectMany.


I recently developed implementation of ADFS attribute store that calls into backend web service to retrieve data. I wanted to follow factory -> client approach rather than reusing client for each call, so I ended up in 2 level async calls as illustrated in simplified code sample below:

public class IMyAttributeStore : IAttributeStore
{
    ChannelFactory<IMyBackendInterface> factory;
    public IMyAttributeStore()
    {
    }

    public IAsyncResult BeginExecuteQuery(string query, string[] parameters, AsyncCallback callback, object state)
    {
        AsyncResult queryResult = new TypedAsyncResult<string[][]>(callback, state);
        var client = factory.CreateChannel();
        CallState cs = new CallState(client, queryResult);

        Request rq = new Request();

        client.BeginGetUserRoles(rq, new AsyncCallback(AsyncCallCallback), cs);

        return cs.result;
    }

    public string[][] EndExecuteQuery(IAsyncResult result)
    {
        return TypedAsyncResult<string[][]>.End(result);
    }

    // Initialize state here.
    public void Initialize(Dictionary<string, string> config)
    {
        var endpoint = config["endpointConfigurationName"];
        factory = new ChannelFactory<IMyBackendInterface>(endpoint);
    }

    void AsyncCallCallback(IAsyncResult result)
    {
        CallState cs = (CallState)result.AsyncState;

        Response data = cs.client.EndGetUserRoles(result);
        List<string[]> claimData = new List<string[]>();
        foreach (var val in data.Values)
            claimData.Add(new string[1] { val });
        string[][] retVal = claimData.ToArray();

        TypedAsyncResult<string[][]> queryResult = (TypedAsyncResult<string[][]>)cs.result;
        queryResult.Complete(retVal, false);
    }
}

class CallState
{
    public IMyBackendInterface client;
    public AsyncResult result;

    public CallState(IMyBackendInterface c, AsyncResult r)
    {
        client = c;
        result = r;
    }
}

Wondering if this is a good pattern to follow, or someone found out better one meanwhile?

0

精彩评论

暂无评论...
验证码 换一张
取 消