在作为parralel downloadmanager工作的双工WCF服务中,我有提供每个下载状态的Downloader类和包含Downloader instanses的ObservableCollection。当我尝试通过BinaryFormatter序列化ObservableCollection然后我得到SerializationException并带有消息:"在Assembly \ System.Version 4.0.0.0中输入\ System.Net.ConnectStream \,Culture = neutral,PublicKey Token = b77a5c561934e089 \未标记为序列化&#34 ;.下面是Downloader类的condenced版本:
[Serializable()]
public class Downloader
{
/// <summary>
/// "Download status changed" event.
/// </summary>
[field: NonSerialized()]
public event EventHandler<InServiceHandledDownloadStatusChangedEventArgs> InServiceHandledDownloadStatusChanged;
/// <summary>
/// URL of downloaded resource.
/// </summary>
private String _targetUrl;
/// <summary>
/// Path to save downloaded data on local drive.
/// </summary>
private String _pathToSave;
/// <summary>
/// The number of bytes downloaded from internet resource.
/// </summary>
private Int64 _downloadedBytesQuantity = 0;
/// <summary>
/// Current status of downloading ("Await", "Running", "Completed").
/// </summary>
private String _status = "Added";
/// <summary>
/// Task that performs download.
/// </summary>
[NonSerialized()]
public Task TaskPerformingDownload;
/// <summary>
/// The source of cancellation token for cancelling of TaskPerformingDownload.
/// </summary>
[NonSerialized()]
public CancellationTokenSource CancelDownloadTokenSource;
/// <summary>
/// Gets or sets stream to read downloaded data from internet.
/// </summary>
public Stream ReadСontentStream { get; set; }
/// <summary>
/// Gets or sets stream to write downloaded data to local drive.
/// </summary>
public Stream SaveСontentStream { get; set; }
/// <summary>
/// This method executes in TaskPerformingDownload and performs downloading.
/// of a resource from internet.
/// </summary>
/// <param name="p_CancellationToken">Cancellation Token</param>
public void PerformDownload(Object p_CancellationToken)
{
try
{
// Get cancellation token.
CancellationToken cancellationToken = (CancellationToken)p_CancellationToken;
// Check was the task canceled?
cancellationToken.ThrowIfCancellationRequested();
. . . . . . . .
HttpWebRequest webResourceRequest = (HttpWebRequest)WebRequest.Create(TargetUrl);
HttpWebResponse webResourceResponse = (HttpWebResponse)webResourceRequest.GetResponse();
this.ReadСontentStream = webResourceResponse.GetResponseStream();
this.SaveСontentStream = new FileStream(this.PathToSave, FileMode.Create, FileAccess.Write, FileShare.None);
int bytesReceivedInChank = 0;
byte[] downloadBuffer = new byte[2048];
// The downloading loop.
while ((bytesReceivedInChank = this.ReadСontentStream.Read(downloadBuffer, 0, downloadBuffer.Length)) > 0)
{
if (cancellationToken.IsCancellationRequested)
cancellationToken.ThrowIfCancellationRequested();
this.SaveСontentStream.Write(downloadBuffer, 0, bytesReceivedInChank);
. . . . . . . .
}
}
catch(Exception){. . . .}
finally
{
if (this.ReadСontentStream != null)
{
this.ReadСontentStream.Close();
this.ReadСontentStream.Dispose();
}
if (this.SaveСontentStream != null)
{
this.SaveСontentStream.Close();
this.SaveСontentStream.Dispose();
}
}
}
}
TaskPerformingDownload成员是执行一次下载的TPL任务。它是从StartDownload()契约方法开始的,WCF服务在客户端请求时调用该方法。在此任务中执行PerformDownload方法。 WCF服务创建尽可能多的下载程序实例,因为必须执行许多下载。每次下载一个Downloader实例。 带有消息的SerializationException异常:&#34;在assembly \ System.Version 4.0.0.0中的Type \ System.Net.ConnectStream \,Culture = neutral,PublicKey Token = b77a5c561934e089 \未标记为可序列化&#34;仅当我尝试使用已完成的下载序列化ObservableCollection时才会发生。下载完成后,其任务(TaskPerformingDownload成员)也已完成其工作,并且不再执行。我尝试在完成下载中处理任务,但它没有帮助,并且仍然存在SerializationException异常。但是如果ObservableCollection中只有新的下载,那么尚未运行的下载(以便此下载的TaskPerformingDownload成员尚未运行),在这种情况下,ObservableCollection序列化很好,没有任何例外或错误。你可以给我一些提示,为什么会出现这种情况?这对我来说非常重要。
答案 0 :(得分:4)
您正在尝试序列化Stream
。这是灾难的一个方法:流不是数据桶 - 它们是管道;如果有的话,它们很少以任何有意义的方式序列化。我的建议很简单:不要那样做。实际上,不要尝试完全序列化Downloader
;你应该序列化数据,而不是实现状态。创建一个单独的模型,该模型仅用于序列化,并且包含您希望以最简单的方式存储的数据。如果要序列化二进制请求/响应:byte[]
。等等。
此外:由于多种原因,我强烈建议不要在几乎所有情况下使用BinaryFormatter
。
答案 1 :(得分:0)
BinaryFormatter.Serialize方法文档直接说:
在以下情况下抛出SerializationException:
在此期间发生了错误 序列化,例如图形参数中的对象是不是 标记为可序列化。
除了不使用问题对象外,没有什么可以做的。
我不确定为什么你需要将System.Net.ConnectStream序列化 - 它太具体化了上下文(机器)。
您也可以尝试在班级中实施ISerializableInterface。
答案 2 :(得分:0)
如其名称所示的流是流 - 这只是一个持续开启和持续的数据流起始点的指针。如果您已经完成了C / C ++编程,那么您应该知道“这是灾难的秘诀”。 Stream不是数据,而是用于读取和写入任意长度数据的管道或套接字。
如果要序列化从流中获取的数据,则首先获取数据对象的数据,尤其是POCO - Plain Old CLR Object。
例如,
class DownloaderData
{
...
public byte[] ContentData {get;set;}
...
}
并且在类Downloader中,您应该使用stream将所有数据读入字节数组缓冲区。
将数据和处理程序功能分成2个类,可以减少编写较少复杂结构的代码,从长远来看,减少头痛。