Есть ли способ иметь синхронизацию и асинхронную версию одного и того же метода с общим кодом в C #? - PullRequest
1 голос
/ 04 июля 2019

Мне нужно иметь метод, который включает в себя вызов базы данных.Этот метод должен существовать с синхронизирующим вызовом к базе данных (будет внутри SQL Server CLR), а также с асинхронным вызовом базы данных (это будет находиться в клиентском приложении).

В приведенном ниже примере я хочу MyMethodSync иMyMethodAsync для совместного использования как можно большего количества кода, в идеале мне нужен всего 1 метод с bool "call as async".

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;
    }

}

Раздел Попытка SHARED BUSINESS LOGIC - насколько я понял.Очевидно, что в моем приложении методы getR1 и GetR3 намного сложнее.

РЕДАКТИРОВАТЬ - чтобы уточнить: мне нужна версия синхронизации для вызова метода GetDbResult () (представьте, что это какой-то вызов ADO.NET для базы данных SQL Server) и асинхронная для ожидания GetDbResultAsync () (это вызов ядра ASP.NETвеб-сервис).Результат точно такой же, разница только в синхронизации и асинхронном обращении к БД.

Ответы [ 4 ]

1 голос
/ 06 июля 2019

К сожалению, для этого нет идеального решения.На данный момент есть два подхода на полпути:

  1. Ваша операция естественным образом асинхронна (вызов db), поэтому выставляйте только асинхронный API.При необходимости используйте ConfigureAwait(false) везде, чтобы синхронные потребители могли блокировать возвращаемую задачу.
  2. Используйте логический аргумент hack .

Логический аргумент hack работает с помощьюимея базовый метод с параметром bool, со строгой семантикой, согласно которой, если этот параметр указывает, что результат должен быть синхронным, тогда базовый метод должен вернуть уже выполненную задачу.Это позволяет вызывающему коду безопасно блокировать его:

public class MyClass
{
  const string SQL = "SELECT Value FROM Table1 WHERE UniqueCode = @I";
  private async Task<int> MyMethodCore(int i, bool sync)
  {
    var r1 = i + 4;
    var r2 = sync ? MockDbAccess.GetDbResult(r1, SQL) : await MockDbAccess.GetDbResultAsync(r1, SQL);
    var r3 = r1 + r2 * 7;
    return r3;
  }

  public int MyMethodSync(int i) => MyMethodCore(i, sync: true).GetAwaiter().GetResult();
  public Task<int> MyMethodAsync(int i) => MyMethodCore(i, sync: false);
}
0 голосов
/ 04 июля 2019

Если вы хотите асинхронный вызов:

await MockDbAccess.GetDbResultAsync(r1, SQL);

Если вы хотите синхронизировать вызов:

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

Везде, где вы ожидаете, поставьте это в конец:

.ConfigureAwait (ложь);

Кстати, прочитайте это ASP.NET Core SynchronizationContext

Также прочитайте это: Почему вы должны использовать ConfigureAwait (false)

0 голосов
/ 04 июля 2019

В идеале мне нужен только 1 метод с bool "call async".

Я думаю, что это выполнимо.В приведенном ниже примере безопасно вызывать Result задачи, возвращаемой с async: false, потому что внутри гибридного метода ожидание не происходит, поэтому задача выполняется синхронно.

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);
0 голосов
/ 04 июля 2019

Два метода будут иметь совершенно разные возвращаемые типы (bool vs Task ) и имена (всегда суффикс с Async), поэтому вы не можете / не должны иметь одну функцию, которая выполняет обе цели.

При этом, если вы реализуете асинхронную версию, то синхронная версия может просто вызвать асинхронную версию:

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

public bool MyFunc()
{
   return MyFuncAsync().Result;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...