0

Подскажите, как можно конвертировать void в Task.

Task асинхронная отправка, и в отправке файле, как например в данном примере, неплохо было бы иметь именно Task, а не void.

Помогите понять какой возврат дать методу и как правильно изменить, чтобы ниже приведенный void преобразовать в Task.

public void UploadFile(string localFilePath, string remoteFilePath)
{
    // Добавляем ключи.
    var keyFiles = new[]
    {
        new PrivateKeyFile(config.PrivateKeyFilePath, config.PrivateKeyFilePassphrase)
    };
var methods = new List<AuthenticationMethod>();
methods.Add(new PrivateKeyAuthenticationMethod(config.UserLogin, keyFiles));

// Создаем клиента.
var connexionWithRSA = new ConnectionInfo(config.Host, config.Port, config.UserLogin, methods.ToArray());
using var client = new SftpClient(connexionWithRSA);

try
{
    client.Connect();

    try
    {
        // Отправляем файл.
        using var stream = File.OpenRead(localFilePath);
        client.UploadFile(stream, remoteFilePath, true); // true для readOnly
    }
    catch (Exception exception)
    {
        logger.LogInformation(exception, $"Failed in uploading file [{localFilePath}] to [{remoteFilePath}]");
    }
    finally
    {
        client.Disconnect();
    }
}
catch (System.Net.Sockets.SocketException)
{
    logger.LogInformation($"Invalid {config.Host} or {config.UserLogin}");
}
catch (Renci.SshNet.Common.SshAuthenticationException exception)
{
    logger.LogInformation(exception.Message);
}

finally
{
    client.Disconnect();
}

}

public Task UploadFileAsync(string localFilePath, string remoteFilePath)
{
   ... 
}
Kromster
  • 13,809
Dev18
  • 640
  • 3
    если внутри функции выполняются только синхронные операции, нет смысла менять void на task – Grundy Aug 20 '20 at 09:08
  • using var stream = File.OpenRead(localFilePath); - а тут похоже синтаксическая ошибка – Grundy Aug 20 '20 at 09:09
  • 2
    Есть асинхронные версии методов Connect, Disconnect, UploadFile? Если есть - используйте их. И тогда ставьте Task в сигнатуру. – Alexander Petrov Aug 20 '20 at 10:39
  • 1
    @Grundy, это правильный синтаксис из C# 8 – Exploding Kitten Aug 20 '20 at 11:22
  • 1
    @ExplodingKitten, да, пропустил это нововведение ¯\(ツ) – Grundy Aug 20 '20 at 11:27
  • @AlexanderPetrov, к сожалению нету, но необходимо конвертировать, чтобы метод был асинхронный и задачи внутри хотелось бы изменить на асинхронные, пока не пойму как, потому что по умолчанию библиотека ssh.net предоставляет только синхронные методы, но хотелось бы возврата Task, при оотправлении файла, если например сбой, или файл велик в дальнейшем это решение интересно, в данном примере код рабочий и синтаксис проверен, спасибо всем за отзывы и комментарии, не пойму только почему проголосоловали против этого вопроса – Dev18 Aug 20 '20 at 12:21
  • 1
    Значит, оставьте этот метод синхронным. А в месте вызова оборачивайте его в Task.Run – Alexander Petrov Aug 20 '20 at 12:22
  • как вариант я это решение рассматриваю, но хотелось бы знать если кто сталкивался с похожей задачей, то было бы неплохо получить решение, конверта void в Task, так как данный метод будет завернут в библиотеку и все таки случай интересен – Dev18 Aug 20 '20 at 12:25
  • 1
    С подобной задачей сталкивались почти все (ну, многие). Правильным решением будет оставить метод синхронным. А в месте вызова оборачивать его в Task. Не надо это делать внутри метода. Task.Run Etiquette – Alexander Petrov Aug 20 '20 at 12:39
  • 2

1 Answers1

2

Как уже упомянуто в комментариях, способ сделать метод асинхронным есть.

Вот ваш асинхронный метод

public async Task UploadFile(string localFilePath, string remoteFilePath)
{
    // Добавляем ключи.
    var keyFiles = new[]
    {
        new PrivateKeyFile(config.PrivateKeyFilePath, config.PrivateKeyFilePassphrase)
    };
var methods = new List<AuthenticationMethod>();
methods.Add(new PrivateKeyAuthenticationMethod(config.UserLogin, keyFiles));

// Создаем клиента.
var connexionWithRSA = new ConnectionInfo(config.Host, config.Port, config.UserLogin, methods.ToArray());
using var client = new SftpClient(connexionWithRSA);

try
{
    await Task.Run(() => client.Connect());

    try
    {
        // Отправляем файл.
        using var stream = File.OpenRead(localFilePath);
        await Task.Run(() => client.UploadFile(stream, remoteFilePath, true)); // true для readOnly
    }
    catch (Exception exception)
    {
        logger.LogInformation(exception, $"Failed in uploading file [{localFilePath}] to [{remoteFilePath}]");
    }
}
catch (System.Net.Sockets.SocketException)
{
    logger.LogInformation($"Invalid {config.Host} or {config.UserLogin}");
}
catch (Renci.SshNet.Common.SshAuthenticationException exception)
{
    logger.LogInformation(exception.Message);
}
finally
{
    client.Disconnect();
}

}

Еще убрал лишний Disconnect()

Я привел самый простой пример с инкапсуляцией синхронных методов в Task, но по-хорошему, здесь нужно реализовать правильную обертку для асинхронных EAP методов SftpClient (например BeginUploadFile и EndUploadFile) в формат TAP. Это было бы идеальным решением в данном контексте.

Вот, на мой взляд, хороший пример EAP -> TAP преобразования. Еще документация.

aepot
  • 49,560
  • Я понимаю, что на английском SO даунвотеры не комментят, чтобы в обратку минусов не наполучать, но здесь то вроде все одни и те же, и я, насколько мне известно, не был замечен в невменяемости, можно уточнить, за что минус? – aepot Aug 20 '20 at 15:10
  • После прочтения блога stephencleary, появились некие сомнения, которые пока не развеянны полностью, за не полным понимаем возможно, Скажите, а какая разница между public async Task UploadFile(string localFilePath, string remoteFilePath) и public Task UploadFile(string localFilePath, string remoteFilePath) что меняет async к Task кроме как await в методе – Dev18 Aug 20 '20 at 15:11
  • 2
    @Dev18 async генерит машину состояний, которая в свою очередь позволяет использовать ключевое слово await. В конкретно данном в ответе случае без async никак. Избавиться от async машины можно только в том случае, если await в методе один, это последняя строчка кода метода. Так же не получится избавиться от async, если вызов находится внутри директивы using или try-catch, то есть здесь ни одно из условий возможной оптимизациии для избавления от машины состояний не выполняется. – aepot Aug 20 '20 at 15:13
  • Минус мой. Я не говорю, что ответ неправильный. Но причина как раз в том, что в таком варианте создаются лишние машины состояний. Обернуть весь код целиком в Task.Run будет эффективней (возможно). | Под асинхронностью обычно понимают IO-bound операции. А тут создаются CPU-bound таски. Поэтому сигнатура метода несколько вводит в заблуждение. – Alexander Petrov Aug 20 '20 at 15:56
  • @AlexanderPetrov по идее нужно просто обернуть EAP API в TAP, но зря вы думаете что машина - плохо, да она дает оверхед, но настолько минимальный, что его нельзя игнорировать только в случаях, когда сама машина по себе не несет пользы. Почему весь метод я бы не стал оборачивать - потому что инициализацию объектов я бы произвел здесь синхронно - я не знаю, насколько потокобезопасные методы используются для инициализации, async считаю здесь оправданным. Верно, по факту здесь засунуты I/O в поток, но по словам того же S. Cleary об этом стоит париться в ASP.NET, а в десктопе он сам этим грешит. – aepot Aug 20 '20 at 16:07
  • Узнала некие термины и абривиатуры за утро, благодаря вашим комментам, EAP API в TAP Асинхронная модель на основе событий ===> Асинхронная модель на основе задач в моем примере ведь перегрузка библиотеки(ssh.net) с синхронными методами, а не асинхронная модель на основе событий или в данном комментарии речь идет о возможном апиПриложении, которое обернуто в "синхронная модель на основе задач" TAP, не совсем понятно все таки рассуждения, и если честно все еще продолжаю поиски, и ищу как программисты используют SSH.NET. – Dev18 Aug 21 '20 at 07:49
  • в моем случае, я создаю библиотеку, которая будет универсальной и вызывать с различных программ те или иные методы из данной библиотеки, именно для отправки файлов, чаще всего и скорей всего будет использоваться асинхронная передача, и поэтому вопрос "Предоставить выбор пользователю выбирать" будет однозначным, то есть вызов будет только асинхронный, вопрос в другом, как правильно для машины и как быстро и качественно для клиента – Dev18 Aug 21 '20 at 07:53
  • @Dev18 SSH.NET SftpClient поддерживает асинхронность нативно, но на основе EAP, коневертировать это в TAP - задача простая и типовая. I/O операции по умолчанию должны быть асинхронными, это даже не обсуждается. Например в классе HttpClient и обслуживающих его классах вы не найдете вообще ни одного синхронного метода для передачи данных, и это как бы правильно и нормально. – aepot Aug 21 '20 at 09:45