Как сделать несколько асинхронных вызовов и вернуть данные из первого вызова, который находит соответствующую запись, а затем прекратить оставшиеся асинхронные вызовы - PullRequest
1 голос
/ 28 июня 2019

Мне нужно сделать несколько вызовов в БД, просматривая строки подключения. В базе данных есть только 1 соответствующая запись, и если я найду соответствующую запись, я смогу вернуть данные и отменить другие асинхронные вызовы.

using (var Contexts = instContextfactory.GetContextList())
{
    foreach(var context in Contexts.GetContextList())
    {    
        // how do I make all the calls and return data from the first call that finds data and continue with further process.(don't care about other calls if any single call finds data.           
        context.Insurance.GetInsuranceByANI(ani);
    }
}

GetInsuranceByANI

public Task<IEnumerable<Insurance>> GetInsuranceByANI(string ani)
{
    using (ITransaction transaction = Session.Value.BeginTransaction())
    {
        transaction.Rollback();
        IDbCommand command = new SqlCommand();
        command.Connection = Session.Value.Connection;

        transaction.Enlist(command);

        string storedProcName = "spGetInsurance";

        command.CommandText = storedProcName;
        command.Parameters.Add(new SqlParameter("@ANI", SqlDbType.Char, 0, ParameterDirection.Input, false, 0, 0, null, DataRowVersion.Default, ani));

        var rdr = command.ExecuteReader();
        return Task.FromResult(MapInsurance(rdr));
    }
}

Например: я перебираю с 5 (a, b, c, d, e) разными строками соединения дБ. Мне нужно сделать асинхронные звонки на все 5 дБ. Если я нахожу соответствующую запись в db: b, тогда я могу вернуть эти данные и перейти к следующим шагам и прекратить звонить другим dbs

Ответы [ 2 ]

2 голосов
/ 28 июня 2019

Как насчет возврата значения, как только вы его получите. Не позволяя потоку двигаться вперед и разрывать петлю.

using (var Contexts = instContextfactory.GetContextList())
    {
           foreach(var context in Contexts.GetContextList())
           {    
               // how do I make all the calls and return data from the first call that finds data and continue with the further process.(don't care about other calls if any single call finds data.           
                var result = await context.Insurance.GetInsuranceByANI(ani);

                if(result.Any())
                {
                    return result.First();
                }
           }
    }
0 голосов
/ 28 июня 2019

Чтобы упростить задачу, вы должны сначала изменить свой GetInsuranceByANI метод, чтобы снова стать синхронным. Позже мы будем вызывать задачи для асинхронного вызова.

public IEnumerable<Insurance> GetInsuranceByANI(string ani)
{
    using (ITransaction transaction = Session.Value.BeginTransaction())
    {
        transaction.Rollback();
        IDbCommand command = new SqlCommand();
        command.Connection = Session.Value.Connection;

        transaction.Enlist(command);

        string storedProcName = "spGetInsurance";

        command.CommandText = storedProcName;
        command.Parameters.Add(new SqlParameter("@ANI", SqlDbType.Char, 0, ParameterDirection.Input, false, 0, 0, null, DataRowVersion.Default, ani));

        var rdr = command.ExecuteReader();
        return MapInsurance(rdr);
    }
}

Теперь к реализации метода, который ищет все базы данных асинхронно. Мы создадим задачу для каждой базы данных, работающей в потоке пула потоков. Это спорно, но мы пытаемся держать вещи простыми. Мы также создаем экземпляр CancellationTokenSource и передаем его Token всем Task.Run методам. Это только гарантирует, что после того, как мы получим наш результат, больше никаких задач не начнется. Если доступные потоки в пуле потоков больше, чем базы данных для поиска, все задачи начнутся немедленно, и маркер отмены фактически ничего не отменит. Другими словами, все запущенные запросы будут выполнены независимо от того, что. Это, очевидно, пустая трата ресурсов, но, опять же, мы пытаемся упростить ситуацию.

После запуска задач мы входим в цикл, ожидающий завершения следующей задачи (используя метод Task.WhenAny). Если результат найден, мы отменяем токен и возвращаем результат. Если результат не найден, мы продолжаем цикл для следующего результата. Если все задачи завершены, а у нас по-прежнему нет результата, мы возвращаем ноль.

async Task<IEnumerable<Insurance>> SearchAllByANI(string ani)
{
    var tasks = new HashSet<Task<IEnumerable<Insurance>>>();
    var cts = new CancellationTokenSource();
    using (var Contexts = instContextfactory.GetContextList())
    {
        foreach (var context in Contexts.GetContextList())
        {
            tasks.Add(Task.Run(() =>
            {
                return context.Insurance.GetInsuranceByANI(ani);
            }, cts.Token));
        }
    }
    while (tasks.Count > 0)
    {
        var task = await Task.WhenAny(tasks);
        var result = await task;
        if (result != null && result.Any())
        {
            cts.Cancel();
            return result;
        }
        tasks.Remove(task);
    }
    return null;
}

Пример использования:

IEnumerable<Insurance> result = await SearchAllByANI("12345");
if (result == null)
{
    // Nothing fould
}
else
{
    // Do something with result
}
...