user3769902
user3769902

Reputation: 445

SerializationException: "Type\...\ in assembly\...\ is not marked as serializable" appears when I try to serialize ObservableCollection

In duplex WCF service that works as parralel downloadmanager I have Downloader class that presents each download status and an ObservableCollection that contains Downloader instanses. When I try to serialize ObservableCollection via BinaryFormatter then I get SerializationException with message: "Type\System.Net.ConnectStream\ in assembly\System.Version 4.0.0.0, Culture=neutral, PublicKey Token=b77a5c561934e089\ is not marked as serializable". Below is condenced version of Downloader class:

[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 member is a TPL task performing one download. It is started from StartDownload() contract method which WCF service calls when client asks to do it. PerformDownload method is executed in this task. WCF service create as many Downloader instances as many downloads must be performed. One Downloader instance per each download. A SerializationException exception with message: "Type\System.Net.ConnectStream\ in assembly\System.Version 4.0.0.0, Culture=neutral, PublicKey Token=b77a5c561934e089\ is not marked as serializable" occurs only when I try to serialize ObservableCollection with completed downloads. When download is completed its task (TaskPerformingDownload member) has completed its work too and has no more executed. I try to dispose task in completed download but it doesn't help and SerializationException exception remains. But if there are only new downloads in the ObservableCollection so the downloads that havn't been run yet (so that the TaskPerformingDownload member of this download hasn't run yet) that in that case the ObservableCollection is serialized nice without any exceptions or errors. Can you give me any tip why this situation has place? It is very important for me.

Upvotes: 1

Views: 2069

Answers (3)

ZZZ
ZZZ

Reputation: 2812

Stream as its name indicated, is stream -- which is just a pointer of starting point a data stream lasting on and on. If you have done C/C++ programming, you should know "That is a recipe for disaster". Stream is NOT data, but a pipe or socket for reading and writing arbitrary length of data.

If you want to serialize the data obtained from the stream, then get the data first for the data object, particularly POCO - Plain Old CLR Object.

For example,

class DownloaderData
{
...
public byte[] ContentData {get;set;}
...
}

and in class Downloader, you should use stream to read all the data into the byte array buffer.

Separating the data and the handler function into 2 classes will make your write less codes in less complex structure with less headache in long run.

Upvotes: 0

Eugene Podskal
Eugene Podskal

Reputation: 10401

BinaryFormatter.Serialize method documentation directly says:

SerializationException is thrown when:
An error has occurred during serialization, such as if an object in the graph parameter is not marked as serializable.

There is nothing you can do short of not using the problem objects.

I am not sure even why would you need the System.Net.ConnectStream to be serialized - it is too context (machine)specific.

Also you may try to implement the ISerializableInterface in your class.

Upvotes: 0

Marc Gravell
Marc Gravell

Reputation: 1062905

You are trying to serialize a Stream. That is a recipe for disaster: streams are not buckets of data - they are pipes; they are very rarely serializable in any meaningful way, if at all. My advice simply: don't do that. In fact, don't try serializing Downloader at all; you should serialize data, not the implementation state. Create a separate model that is simply intended for serialization, and which contains the data you want to store in the simplest most obvious way. If you want to serialize a binary request / response: byte[]. And so on.

Also: I strongly advise against using BinaryFormatter in virtually all scenarios, for multiple reasons.

Upvotes: 4

Related Questions