2017-03-28 2 views
1

J'ai développé un petit projet (en utilisant MVVM) avec des fonctionnalités permettant de télécharger un fichier sur un serveur FTP.Annulation de tâche: lorsque la boucle n'est pas fermée correctement lorsque ThrowIfCancellationRequested() est appelée

L'utilisateur peut voir la progression du téléchargement: le pourcentage complété est affiché à l'utilisateur par vm.Busycontent, qui est une propriété dans mon viewmodel lié à un élément de l'interface utilisateur dans ma vue.

Voici le code pour lire le fichier et le téléchargement via FTP (qui fait partie du Groupe vm.FtpUploadTask)

using (FileStream inputStream = File.OpenRead(file)) 
     { 
      using (outputStream = request.GetRequestStream()) 
      { 
       var buffer = new byte[1024 * 1024]; 
       int totalReadBytesCount = 0; 
       int readBytesCount; 

       while ((readBytesCount = inputStream.Read(buffer, 0, buffer.Length)) > 0 && (!vm.Token.IsCancellationRequested)) 
       { 
        vm.Token.ThrowIfCancellationRequested(); 

        outputStream.Write(buffer, 0, readBytesCount); 
        totalReadBytesCount += readBytesCount; 
        var progress = totalReadBytesCount * 100.0/inputStream.Length; 
        vm.BusyContent = ((int)progress).ToString(); 
       } 
      } 
     }    

MainWindow.xaml

J'utilise boîte à outils WPF étendu BusyIndicator

<xctk:BusyIndicator IsBusy='{Binding IsBusy}'> 
    <xctk:BusyIndicator.BusyContentTemplate> 
    <DataTemplate> 
     <StackPanel> 
     <TextBlock Text='{Binding Path=DataContext.BusyContent, 
      RelativeSource={RelativeSource AncestorType={x:Type Window}}}' /> 
     </StackPanel> 
    </DataTemplate> 
    </xctk:BusyIndicator.BusyContentTemplate> 
</xctk:BusyIndicator>  

UploadToFTPCommand.cs

try 
{ 
    vm.FtpUploadTask = new Task(() => FTPUpload(file), vm.Token); 

    vm.FtpUploadTask.Start(); 
    vm.FtpUploadTask.Wait(vm.Token); 

    vm.BusyContent = "upload done!"; 

} 
catch (OperationCanceledException) 
{ 
    vm.BusyContent = "Canceled"; 
} 

CancelCommand.cs

public class CancelCommand : ICommand 
{ 
    public void Execute(object parameter) 
    { 
     vm.TokenSource.Cancel(); 
    } 
} 

Les travaux de fonction d'annulation mais parfois vm.Busycontent égale à

  • ((int) progrès) .ToString()
  • annulé dans uploadftpc

Lorsque vous appuyez sur le bouton Annuler, la boucle while doit être fermée et l'utilisateur doit uniquement voir le message dans la capture (OperationCanceledException). Des idées pour résoudre ce problème?

Remarques

  • J'utilise .NET 4.0
  • Ce programme est une partie d'un projet plus vaste, qui comprend les tâches multipe qui doivent être exécutées de manière synchrone. C'est pourquoi j'utilise les méthodes Task.Start() et Task.Wait().

Modifier problème reste

using (FileStream inputStream = File.OpenRead(file)) 
{ 
using (outputStream = request.GetRequestStream()) 
{ 
    var buffer = new byte[1024 * 1024]; 
    int totalReadBytesCount = 0; 
    int readBytesCount; 

    while ((readBytesCount = inputStream.Read(buffer, 0, buffer.Length)) > 0) 
    { 
     if (vm.Token.IsCancellationRequested) 
     { 
      break; 
     } 
     outputStream.Write(buffer, 0, readBytesCount); 
     totalReadBytesCount += readBytesCount; 
     var progress = totalReadBytesCount * 100.0/inputStream.Length; 
     vm.BusyContent = ((int)progress).ToString(); 
    } 
    if (vm.Token.IsCancellationRequested) 
    { 

     inputStream.Close(); 
     outputStream.Close(); 
     vm.Token.ThrowIfCancellationRequested(); 
    } 
} 

}

+1

Vous vérifiez 'Token.IsCancellationRequested' et vous quittez la boucle avant d'exécuter 'ThrowIfCancellationRequested()'. – JSteward

+1

C'est beaucoup de code, pourriez-vous poster un [mcve]? – svick

+0

@svick Je modifie ma question pour réduire la quantité de code – BertAR

Répondre

0

Vous pouvez avoir un problème relatif à exactement comment vous avez configuré votre code. Sans passer par chaque ligne, c'est difficile à dire. Mais plus au point:

Lorsque vous appuyez sur le bouton Annuler, la boucle while doit être fermée et l'utilisateur doit uniquement voir le message dans le catch (OperationCanceledException).

Ceci montre exactement comment quitter votre boucle while et lancer une exception que vous pouvez attraper.Notez le test affirme TaskCanceledException mais vous avez raison d'attraper OperationCanceledException.

using System.Threading.Tasks; 
using System.IO; 
using System.Threading; 

using NUnit.Framework; 

namespace CancellationTests { 

    [TestFixture] 
    public class WhileCancellation { 

     [Test] 
     public void While_Loop_Is_Canceled_When_Cancel_Is_Requested() { 
      var inputFile = new FileInfo("somebigfile.txt"); 
      var outputFile = new FileInfo("outputFile.txt"); 
      var cts = new CancellationTokenSource(); 

      cts.Cancel(); 

      Assert.ThrowsAsync<TaskCanceledException>(() => TheWhileLoop(inputFile, outputFile, cts.Token)); 
     } 

     private async Task TheWhileLoop(FileInfo inputFile, FileInfo outputFile, CancellationToken token) { 

      using (var inputStream = inputFile.OpenRead()) 
      using (var outputStream = outputFile.OpenWrite()) { 
       var buffer = new byte[1024 * 1024]; 
       var totalReadBytesCount = 0; 
       var readBytesCount = 0; 

       while ((readBytesCount = await inputStream.ReadAsync(buffer, 0, buffer.Length, token)) > 0) {      
        await outputStream.WriteAsync(buffer, 0, readBytesCount, token); 
        token.ThrowIfCancellationRequested(); 
        totalReadBytesCount += readBytesCount; 
        var progress = totalReadBytesCount * 100.0/inputStream.Length; 
        vm.BusyContent = ((int)progress).ToString(); 
       } 
      } 
     }   

     private static class vm { 
      public static string BusyContent { get; set; } 
     } 
    }  
} 

Espérons que cela vous met sur la bonne voie.