I have a C# (WPF) application which consumes a particular 3rd party API/tool (let's call this Tool A). My colleagues and I are trying to decouple that from our application, so that it is possible to switch out Tool A in the future, replacing with another vendor's API/tool.
We decided to define a set of interfaces, which would provide the main functionalities of the tool. For every 3rd party API/tool that we want to use to replace Tool A, they would need to implement an adapter (or a set of adapters) that implements the interface(s). This also allows us to use this tool for other projects/applications.
Some of the functionalities provided by Tool A are as non-async
(e.g. public Foo DoSomething(Bar bar)
) methods. We are considering to define interface methods that provide such functionalities as async
method (e.g. Task<Foo> DoSomethingAsync(Bar bar)
, i.e. Task
-returning methods).
That means there are three choices here:
- Define these methods as non-
async
only. - Define these methods as
async
only. - Define both non-
async
andasync
methods..
Things to consider:
There are also methods which we thought would make sense to be implemented as non-
async
methods, but surprisingly the vendor only provided anasync
version of it.When we force an
async
method to be synchronous (byTask.Result
orTask.Wait()
), we are likely to get deadlocks.Non-
async
methods can be easily madeasync
.Some of the methods are likely to take some time to execute (likely CPU-bound), but that's likely to be between 100-2000ms (i.e. not terribly slow either). Actual result might depend on how the vendor implements it. Implementations of the adapter/interface can choose to:
a. Call the tool's methods synchronously. (e.g.
return Task.FromResult(DoSomething());
)b. Wrap with
Task.Run()
orTaskFactory.StartNew()
.I have read this article from Stephen Toub, but I'm not sure whether my case is similar to the
Stream
example.
Example:
public class MainApp
{
private ITool Tool; // Injected in
public Task<Foo> GetFooFromBarAsync(Bar bar)
{
return Tool.DoSomethingAsync(bar);
}
}
public interface ITool
{
Task<Foo> DoSomethingAsync(Bar bar);
}
public class ToolAdapterA : ITool
{
// Private fields of objects of 3rd party API's namespaces
public Task<Foo> DoSomethingAsync(Bar bar)
{
return Task.FromResult(ToolFromVendorA.DoSomething(bar));
}
}
public class ToolAdapterB : ITool
{
// Private fields of objects of second 3rd party API's namespaces
public Task<Foo> DoSomethingAsync(Bar bar)
{
return ToolFromVendorB.ICanDoSomethingInAsync(bar);
}
}
Which of the three choices are most appropriate for my case, based on the considerations I have (+ any other considerations that might be valid)?