SerializationException:当我尝试序列化ObservableCollection时出现“在程序集\ ... \中未键入\ ... \标记为可序列化”

时间:2014-07-04 11:18:30

标签: c# wcf serialization

在作为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序列化很好,没有任何例外或错误。你可以给我一些提示,为什么会出现这种情况?这对我来说非常重要。

3 个答案:

答案 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个类,可以减少编写较少复杂结构的代码,从长远来看,减少头痛。