3

У меня есть следующие два класса в моем приложении Xamarin, который я использую, чтобы загрузить изображение на сервер:ObjectDisposedException при отмене postAsync

public class ProfileApi : IDisposable 
{ 
    public Uri BaseUri { get; private set; } 
    private HttpClientHandler _messageHandler; 

    public ProfileApi (string baseUri, HttpClientHandler messageHandler) 
    { 
     BaseUri = new Uri (baseUri, UriKind.Absolute); 
     _messageHandler = messageHandler; 
    } 

    protected HttpClient CreateHttpClient() 
    { 
     var httpClient = new HttpClient (_messageHandler, true) { 
      BaseAddress = BaseUri 
     }; 
     return httpClient; 
    } 


    public async Task SetProfilePicture (Stream picture, CancellationToken cancellationToken) 
    { 
     using(var client = CreateHttpClient()) 
     { 
      using(var copy = new MemoryStream()) 
      { 
       picture.Position = 0; 
       picture.CopyTo(copy); 
       copy.Position = 0; 

       var content = new StreamContent (copy); 
       content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue ("image/png"); 

       var response = await client.PostAsync ("post", content, cancellationToken);  

       Debug.WriteLine ("Finished!"); 
      } 
     } 
    } 

    public void Dispose() 
    { 
     _messageHandler.Dispose(); 
     _messageHandler = null; 
    } 
} 

public class ProfileDownloader 
{ 
    static readonly Lazy<ProfileDownloader> lazy = new Lazy<ProfileDownloader> (() => 
     new ProfileDownloader() 
    ); 

    public static ProfileDownloader Instance { 
     get { 
      return lazy.Value; 
     } 
    } 

    ProfileDownloader(){} 

    CancellationTokenSource _uploadCancellationSource; 

    public async Task UploadImage (Stream image) 
    { 
     if (AreProfileAndImageBeingUploaded()) { 
      CancelProfileAndImageUpload(); 
      return; 
     } 

     _uploadCancellationSource = new CancellationTokenSource(); 

     using (var ProfileApi = new ProfileApi ("http://httpbin.org", new NativeMessageHandler())) 
     { 
      var imageTask = ProfileApi.SetProfilePicture (image, _uploadCancellationSource.Token); 

      try { 

       await imageTask; 
       _uploadCancellationSource = null; 

      } catch (OperationCanceledException ex) { 
       Debug.WriteLine (string.Format ("Cancelled! : {0}", ex.Message)); 

      } catch (Exception ex) { 
       Debug.WriteLine (string.Format ("Exception! : {0}, {1}", ex.Message, ex.Source)); 
      } 
     } 
    } 

    public bool AreProfileAndImageBeingUploaded() 
    { 
     return _uploadCancellationSource != null; 
    } 

    public void CancelProfileAndImageUpload() 
    { 
     if (_uploadCancellationSource != null) { 
      _uploadCancellationSource.Cancel(); 
      _uploadCancellationSource = null; 
     } 
    } 
} 

Метод UploadImage вызывается всякий раз, когда я нажимаю кнопку в интерфейсе моего приложения ,

var image = UIImage.FromFile ("Images/SpongeBob.png"); 
using (var stream = image.ToStream()) {   
    ProfileDownloader.Instance.UploadImage (stream); 
} 

Это то, что я делаю:

  1. Нажмите кнопку дважды, что вызывает UploadImage дважды. При первом запуске загрузки, во второй раз он отменяется, и я получаю TaskCancelledException.
  2. Я снова нажимаю кнопку. Первый раз начинается загрузка, а вторая вызывает ObjectDisposedException.

Кто-нибудь знает, почему выбрано исключение, описанное в 2.?

Update

Вот трассировка стека исключения. Он бросается на линию с await imageTask.

 
System.ObjectDisposedException: The object was used after being disposed. at System.Net.WebConnection.EndWrite (System.Net.HttpWebRequest request, Boolean throwOnError, IAsyncResult result) [0x0002c] in 
//Library/Frameworks/Xamarin.iOS.framework/Versions/8.10.1.64/src/mono/mcs/class/System/System.Net/WebConnection.cs:1071 at System.Net.WebConnectionStream.WriteAsyncCB (IAsyncResult r) [0x00013] in 
//Library/Frameworks/Xamarin.iOS.framework/Versions/8.10.1.64/src/mono/mcs/class/System/System.Net/WebConnectionStream.cs:458 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() [0x0000b] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/mcs/class/corlib/System.Runtime.ExceptionServices/ExceptionDispatchInfo.cs:61 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x00047] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:201 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x0002e] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:170 at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x0000b] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:142 at System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter.GetResult() [0x00000] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:477 at System.Net.Http.HttpClientHandler+c__async0.MoveNext() 0x002f4] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/mcs/class/System.Net.Http/System.Net.Http/HttpClientHandler.cs:344 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() [0x0000b] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/mcs/class/corlib/System.Runtime.ExceptionServices/ExceptionDispatchInfo.cs:61 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x00047] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:201 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x0002e] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:170 at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x0000b] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:142 at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1+ConfiguredTaskAwaiter[System.Net.Http.HttpResponseMessage].GetResult() [0x00000] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:580 at System.Net.Http.HttpClient+c__async0.MoveNext() [0x000a9] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/mcs/class/System.Net.Http/System.Net.Http/HttpClient.cs:274 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() [0x0000b] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/mcs/class/corlib/System.Runtime.ExceptionServices/ExceptionDispatchInfo.cs:61 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x00047] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:201 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x0002e] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:170 at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x0000b] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:142 at System.Runtime.CompilerServices.TaskAwaiter`1[System.Net.Http.HttpResponseMessage].GetResult() [0x00000] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:372 at Shared.ProfileApi+c__async0.MoveNext() [0x00114] in 
/Users/mku/Documents/Private projects/Xamarin/Example/Shared/ProfileApi.cs:46 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() [0x0000b] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/mcs/class/corlib/System.Runtime.ExceptionServices/ExceptionDispatchInfo.cs:61 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x00047] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:201 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x0002e] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:170 at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x0000b] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:142 at System.Runtime.CompilerServices.TaskAwaiter.GetResult() [0x00000] in 
/Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:124 at Shared.ProfileDownloader+c__async0.MoveNext() [0x000b1] in 
/Users/mku/Documents/Private projects/Xamarin/Example/Shared/ProfileDownloader.cs:42

ответ

0

Вы должны ждать UploadImage или поток будет расположен сразу после вызова к нему.

Попробуйте это:

var image = UIImage.FromFile ("Images/SpongeBob.png"); 
using (var stream = image.ToStream()) {   
    awaProfileDownloader.Instance.UploadImage (stream); 
} 

Вам придется изменить метод, который содержит этот код на async метода.

+0

Я делаю копию потока в '' SetProfilePicture. Не должна ли копия быть жива до тех пор, пока выполняется веб-запрос? –

+0

Нет. Вы делаете копию ссылки на поток, находящийся в куче. Но это тот же объект. –

+0

Я уверен, что метод 'CopyTo()' действительно копирует сами данные. –

Смежные вопросы