C# have async function call synchronous function or synchronous function call async function

13,911

Solution 1

I want to have both synchronous and asynchronous functions for each operation, as this library will be used by both console and GUI apps, but I don't want to duplicate code everywhere.

The best answer is: don't.

Stephen Toub has two excellent blog posts on this topic:

He recommends exposing asynchronous methods as asynchronous, and synchronous methods as synchronous. If you need to expose both, then encapsulate common functionality in private (synchronous) methods, and duplicate the async/sync differences.

Solution 2

I had a similar situation where some applications needed the data to be loaded synchronously and others asyc. I decided to created an interface that I called my dataloader:

public interface IIMViewModelDL {
    void LoadProjects(AssignProjects callback);
}

The AssignProjects callback is just a simple delegate that takes in the returned list of projects:

public delegate void AssignProjects(IEnumerable<Project> results);

Now, the beauty of this is that you can work with the interface without knowing whether you are dealling in sync or async.

Three classes are created: one base, one sync, and one async:

 public abstract class BaseViewModelDL {
    protected IEnumerable<Project> LoadProjects() {
        BaseServiceClient client = new BaseServiceClient();
        return client.Projects();
    }

public class SynchronousViewModelDL : BaseViewModelDL, IIMViewModelDL {
    public void LoadProjects(AssignProjects callback) {
        callback(base.LoadProjects());
    }

public class AsyncIMViewModelDL : BaseViewModelDL, IIMViewModelDL {
    public void LoadProjects(AssignProjects callback) {
        BackgroundWorker loadProjectsAsync = new BackgroundWorker();
        loadProjectsAsync.DoWork += new DoWorkEventHandler(LoadProjectsAsync_DoWork);
        loadProjectsAsync.RunWorkerCompleted += new RunWorkerCompletedEventHandler(LoadProjectsAsync_RunWorkerCompleted);
        loadProjectsAsync.RunWorkerAsync(callback);
    }

void LoadProjectsAsync_DoWork(object sender, DoWorkEventArgs e) {
        var results = new ObservableCollection<Project>(base.LoadProjects());
        e.Result = new object[] { results, e.Argument };
    }

    void LoadProjectsAsync_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
        AssignProjects callback = (AssignProjects)((object[])e.Result)[1];
        IEnumerable<Project> results = (IEnumerable<Project>)((object[])e.Result)[0];
        callback(results);
    }

Now, in your application, you can decide how you want to load data...this could be injected through an IoC container, but is hard coded for demo purposes:

private ViewModelDataLoaders.IIMViewModelDL dataLoader = new ViewModelDataLoaders.AsyncIMViewModelDL();

Now, your calling code looks the same and is none the wiser to whether it is async or sync:

private void LoadProjects() {
        dataLoader.LoadProjects(
            delegate(IEnumerable<Project> results) {
                Projects = new ObservableCollection<Project>(results);
            });
    }

I use this regularly for unit testing (sync), WPF applications (async), and console applications (sync).

Share:
13,911
deadlydog
Author by

deadlydog

Developer and family man. Loves C#, .NET, XNA, PowerShell, AutoHotkey. Creator of DPSF (XNAParticles.com). Automate Everything! Website: danskingdom.com Blog: blog.danskingdom.com Twitter: @deadlydog

Updated on June 17, 2022

Comments

  • deadlydog
    deadlydog almost 2 years

    I'm writing a C# .Net 4.5 library for doing common sql database operations (backup, restore, execute script, etc.). I want to have both synchronous and asynchronous functions for each operation, as this library will be used by both console and GUI apps, but I don't want to duplicate code everywhere. So as I see it, I have two options:

    1. Write the code that does the work in a synchronous function, and then just wrap it in a task for the async function, like so:

      public void BackupDB(string server, string db)  
      {  
          // Do all of the work and long running operation here  
      }
      
      public async Task BackupDBAsync(string server, string db)  
      {  
          await Task.Factory.StartNew(() => BackupDB(server, db)).ConfigureAwait(false);  
      }
      
    2. Write the code that does the work in an asynchronous function, and call it from a synchronous function using .Wait():

      public async Task BackupDBAsync(string server, string db)  
      {  
          // Do all of the work and long running operation here, asynchronously.  
      }
      
      public void BackupDB(string server, string db)  
      {  
          BackupDBAsync(server, db).Wait(); // Execution will wait here until async function finishes completely.  
      }
      

    Is one option better than the other? Is one a best practice? Or are there any other (better) alternatives?

    I know that one caveat to using .Wait() is that all of the await statements in the async function have to use .ConfigureAwait(false) to avoid deadlocks (as discussed here), but since I'm writing a library that will never need to access the UI or WebContext I am safe to do that.

    I'll note too that the SQL library typically also has both synchronous and async functions that can be used, so if doing the work in the sync function, I would call their sync function, and if doing the work in the async function, I would call their async function.

    Thoughts/suggestions are appreciated.

    -- edit: I've also posted this question on the MSDN forums here to try and get an official MS response --