Silverlight 不支持同步 Web 请求。出于这个原因,我写了Simple Asynchronous Operation Runner。目标之一是能够像编写同步代码一样编写代码,然后对其进行修改以与运行器代码一起使用。
首先从第 1 部分中获取 AsyncOperationService 的一小部分代码,并将其添加到您的项目中(如果您发现这篇文章有点沉重,对实际使用它并不重要,请不要担心)。
使用您已经作为“同步模板”提供的代码,我们可以看到我们需要为 GetRequestStream 和 GetResponseStream 提供几个 AsyncOperation 实现,因此我们将编写这些代码并将它们添加到项目中:-
public static class WebRequestAsyncOps
{
public static AsyncOperation GetRequestStreamOp(this WebRequest request, Action<Stream> returnResult)
{
return (completed) =>
{
request.BeginGetRequestStream((result) =>
{
try
{
returnResult(request.EndGetRequestStream(result));
completed(null);
}
catch (Exception err)
{
completed(err);
}
}, null);
};
}
public static AsyncOperation GetResponseOp(this WebRequest request, Action<WebResponse> returnResult)
{
return (completed) =>
{
request.BeginGetResponse((result) =>
{
try
{
returnResult(request.EndGetResponse(result));
completed(null);
}
catch (Exception err)
{
completed(err);
}
}, null);
};
}
}
现在,如果您正在分块文件上传,您可能希望在 UI 中报告进度,所以我建议您也准备好这个AsyncOperation(注入现有的 AsyncOperationService 类):-
public static AsyncOperation SwitchToUIThread()
{
return (completed => Deployment.Current.Dispatcher.BeginInvoke(() => completed(null)));
}
现在我们可以为您的代码创建一个异步版本:-
IEnumerable<AsyncOperation> Chunker(Action<double> reportProgress)
{
double progress = 0.0;
Chunk chunk = new Chunk();
// Setup first chunk;
while (chunk != null)
{
Stream outStream = null;
HttpWebRequest req = ...
yield return req.GetRequestStreamOp(s => outStream = s);
// Do stuff to and then close outStream
WebResponse response = null;
yield return req.GetResponseOp(r => response = r);
// Do stuff with response throw error is need be.
// Increment progress value as necessary.
yield return AsyncOperationService.SwitchToUIThread();
reportProgress(progress);
chunk = null;
if (moreNeeded)
{
chunk = new Chunk();
// Set up next chunk;
}
}
}
最后你只需要运行它并处理任何错误:-
Chunker.Run((err) =>
{
if (err == null)
{
// We're done
}
else
{
// Oops something bad happened.
}
});