有没有一种方法可以在C#中使用共享代码使同一方法的同步和异步版本?

时间:2019-07-04 08:21:08

标签: c# async-await

我需要一个方法,其中包括对数据库的调用。该方法必须与数据库的同步调用一起存在(将在SQL Server CLR中),并且与数据库的异步调用一起存在(将驻留在客户端应用程序中)。

在下面的示例中,我希望MyMethodSync和MyMethodAsync共享尽可能多的代码,理想情况下,我只希望使用1个带有布尔值“异步调用”的方法。

public static class MockDbAccess
{
    public static int GetDbResult(int i, string sql = null)
    {
        return i + sql?.Length ?? 0;
    }

    async public static Task<int> GetDbResultAsync(int i, string sql = null)
    {
        await Task.Delay(2);
        return i + sql?.Length ?? 0;
    }
}

public class MyClass
{
    const string SQL = "SELECT Value FROM Table1 WHERE UniqueCode = @I";
    // COPY PASTE version:
    public int MyMethodSync(int i)
    {
        var r1 = i + 4;
        var r2 = MockDbAccess.GetDbResult(r1, SQL);
        var r3 = r1 + r2 * 7;
        return r3;
    }
    async public Task<int> MyMethodAsync(int i)
    {
        var r1 = i + 4;
        var r2 = await MockDbAccess.GetDbResultAsync(r1, SQL);
        var r3 = r1 + r2 * 7;
        return r3;
    }

    // SHARED BUSINESS LOGIC attempt:
    int GetR1(int i) => i + 4;
    int GetR3(int r1, int r2) => r1 + r2 * 7;

    public int MyMethodSync_SharedCode(int i)
    {
        var r1 = GetR1(i);
        var r2 = MockDbAccess.GetDbResult(r1, SQL);
        var r3 = GetR3(r1, r2);
        return r3;
    }
    async public Task<int> MyMethodAsync_SharedCode(int i)
    {
        var r1 = GetR1(i);
        var r2 = await MockDbAccess.GetDbResultAsync(r1, SQL);
        var r3 = GetR3(r1, r2);
        return r3;
    }

}

据我所知,“共享业务逻辑”部分的尝试。显然,在我的应用程序中,get GetR1和GetR3方法要复杂得多。

编辑-进行澄清: 我需要同步版本来调用GetDbResult()方法(假设它是对SQL Server db的一些ADO.NET调用),并且需要异步以等待GetDbResultAsync()(这是对ASP.NET核心Web服务的调用)。结果是完全一样的,区别只是对DB的同步调用与异步调用。

4 个答案:

答案 0 :(得分:1)

不幸的是,没有完美的解决方案。目前有两种中途的方法:

  1. 您的操作自然是异步的(db调用),因此仅公开异步API。 (可选)在任何地方使用admin.firestore().collection('notifications').orderBy("date",'desc').limit(1),以便同步使用者可以阻止您返回的任务。
  2. 使用boolean argument hack

布尔参数hack的工作原理是使用带有ConfigureAwait(false)参数的核心方法,严格的语义是,如果该参数指示结果必须是同步的,则核心方法必须返回一个已经完成的任务。这样可以使调用代码安全地对其进行阻止:

bool

答案 1 :(得分:-1)

如果您要进行异步通话:

await MockDbAccess.GetDbResultAsync(r1, SQL);

如果您要同步通话:

MockDbAccess.GetDbResultAsync(r1, SQL).GetAwaiter().GetResult();

任何地方都可以结束此操作:

  

.ConfigureAwait(false);

通过方式阅读此ASP.NET Core SynchronizationContext

也请阅读:Why you should use ConfigureAwait(false)

答案 2 :(得分:-1)

  

理想情况下,我只想使用1个布尔型“异步调用”方法。

我认为这是可行的。在下面的示例中,可以安全地调用用Result返回的任务的async: false,因为Hybrid方法内部没有等待,因此该任务正在同步运行。

private async Task<int> MyMethodHybrid(int i, bool async)
{
    var r1 = i + 4;
    var r2 = async ?
        await MockDbAccess.GetDbResultAsync(r1) :
        MockDbAccess.GetDbResult(r1);
    var r3 = r1 + r2 * 7;
    return r3;
}

public int MyMethod(int i) => MyMethodHybrid(i, false).Result;

public Task<int> MyMethodAsync(int i) => MyMethodHybrid(i, true);

答案 3 :(得分:-3)

这两种方法将具有完全不同的返回类型(bool与Task )和名称(总是带有Async后缀),因此您不能/不应该有一个同时满足这两个目的的函数。

话虽如此,如果您实现了异步版本,那么同步版本只能调用异步版本:

public Task<bool> MyFuncAsync()
{
   return true;
} 

public bool MyFunc()
{
   return MyFuncAsync().Result;
}