简体   繁体   中英

Satisfying an async interface method when you can only run sync

I'm consuming an "API" (I use the term loosely) that's implemented as a COM+ object. As far as I'm aware, there's no support for any of the queuing functionality COM+ offers, but I know pretty much exactly nil about COM+. Take it with a grain of salt. I have just one method available to me: invoke , and it's sync.

In a stroke of somewhat personal brilliance, I created a Web Api wrapper around this COM+ object, which has just one endpoint, invoking the COM+ object with the post body of the request. This has allowed me to make asynchronous calls to the Web Api instead (as well as remove the god-awful dependency from my apps).

Now, I'm creating a library to basically abstract all this mess, but I decided to implement a provider pattern where you can choose to either use the COM+ directly or the Web Api wrapper. I have an interface, obviously, with both sync and async methods. The Web Api provider is of course no problem as you can do an HTTP request either way, but I'm hitting a wall with the COM+ provider. Since async is not an option there.

So, I have three options as I see it:

  1. "Implement" the async interface method with a NotImplementedException , which is a violation of the interface segregation principle.

  2. Do something ill-advised like use Task.Run , which I know is not really async in this context, and basically lies to anyone programming against my library.

  3. Do something like create a sync and async version of the interface, which is better from a interface segregation standpoint, but worse from a provider pattern standpoint. If I depend on the async interface, then the COM+ provider can never be used, and if I depend on the sync interface, then I can never use the async methods of the Web Api provider.

Long and short, my general question is what do you do in a situation where you need to run something asynchronously, but the method you need to utilize can only be run synchronously? If there's no real alternative, then what is the least offensive way to satisfy an interface requirement?

EDIT

I forgot to mention one more option:

  1. Call the method sync inside the async implementation and then simply return Task.FromResult with the result. This means I'm not pulling a new thread (which could be an issue in a web application), but I'm not sure if this is really any better than Task.Run in the sense of lying to the consumer about this actually being "async".

The solution I landed on was to use Task.FromResult to return a Task even though nothing async is being done. While this is not actually async, it satisfies the interface. I then commented the method to note that it will run sync and should be passed as a delegate to Task.Run in situations where the thread cannot be blocked (GUIs, for example). Using Task.Run is not appropriate in every situation (web applications, for one) and is an implementation detail that should be decided upon by the consumer of the library.

That said, to truly implement this, you need to handle three Task scenarios: completed, faulted, and cancelled. Here's the actual code to do all this:

public Task<int> DoSomethingAsync(CancellationToken cancellationToken)  
{
    if (cancellationToken.IsCancellationRequested)
    {
        return Task.FromCanceled<int>(cancellationToken);
    }

    try
    {
        return Task.FromResult<int>(DoSomething());
    }
    catch (Exception e)
    {
        return Task.FromException<int>(e);
    }
}

First, this ensures that the operation hasn't been canceled. If it has, a canceled task is returned. Then, the sync work we need to do is wrapped in a try..catch block. If an exception is thrown, we'll need to return a faulted task, which includes that exception. Finally, if it completes correctly, we return a completed task.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM