Had a situation come up today when we were taking one of our sites SessionState management out of InProc to SqlServer.

The constructor to deserialize an object of type <type> was not found

Any custom object that you have created that you want to stuff into session, when using an out of process session management scheme (such as state server, sql server or your own custom implementation) needs to be decorated with the [Serializable] attribute.

After looking over our various data objects, I made sure that each one was properly decorated.  Since the majority of our data objects are rather simple in nature and contain intrinsic data types, the serializable attribute was all that was needed.

However, we have some core data objects that derive their implementation from DataSet.  Those were the ones with the problem…

   1: [Serializable]
   2: public class MyDataSet : DataSet 
   3: {
   4:     public int ParentRecordId { get; set; }
   5:     public string ConnectionString { get; set; }
   6:     
   7:     public MyDataSet(string connectionString, int parentRecordId) 
   8:     {
   9:         ConnectionString = connectionString;
  10:         ParentRecordId = parentRecordId;
  11:     }
  12: }

 

It was assumed that since DataSet implements ISerializable, then MyDataSet would be alright “out of the box”.  We were equally sure that MyDataSet wouldn’t have to do anything special since it was decorated with the serializable attribute and the custom properties were basic intrinsic types.

… And that’s where we were wrong

It seems that even though DataSet implements ISerializable, we need to have the seralizable constructor in MyDataSet as well.

   1: [Serializable]
   2: public class MyDataSet : DataSet 
   3: {
   4:     public int ParentRecordId { get; set; }
   5:     public string ConnectionString { get; set; }
   6:     
   7:     public MyDataSet(string connectionString, int parentRecordId) 
   8:     {
   9:         ConnectionString = connectionString;
  10:         ParentRecordId = parentRecordId;
  11:     }
  12:     
  13:     protected MyDataSet(SerializationInfo info, StreamingContext context) : base(info, context) { }
  14: }

We made the constructor protected because we don’t want it being called from anywhere but the .Net framework. 

So now the underlying DataSet object of MyDataSet is being serialized and deserialized as expected…  What we discovered was that now, ConnectionString and ParentRecordId was not being properly deserialized any longer.

That being the case, we had to roll up our own serialization utilizing the SerializationInfo object and overriding the GetObjectData() method in the base DataSet object.  Finally, we end up with our class looking like so:

   1: [Serializable]
   2: public class MyDataSet : DataSet 
   3: {
   4:     struct SerializationTags 
   5:     {
   6:         public const string ConnectionString = "MyDataSet_ConnectionString";
   7:         public const string ParentRecordId = "MyDataSet_ParentRecordId";
   8:     }
   9:  
  10:     public int ParentRecordId { get; set; }
  11:     public string ConnectionString { get; set; }
  12:     
  13:     public MyDataSet(string connectionString, int parentRecordId) 
  14:     {
  15:         ConnectionString = connectionString;
  16:         ParentRecordId = parentRecordId;
  17:     }
  18:     
  19:     protected MyDataSet(SerializationInfo info, StreamingContext context) : base(info, context) 
  20:     { 
  21:         ConnectionString = info.GetString(SerializationTags.ConnectionString);
  22:         ParentRecordId = info.GetInt32(SerializationTags.ParentRecordId);
  23:     }
  24:  
  25:     [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]    
  26:     public override void GetObjectData(SerializationInfo info, StreamingContext context)
  27:     {
  28:         base.GetObjectData(info, context);
  29:         info.AddValue(SerializationTags.ConnectionString, ConnectionString);
  30:         info.AddValue(SerializationTags.ParentRecordId, ParentRecordId);
  31:     }
  32:  
  33: }

Hope this helps someone save a headache or two :)