Search code examples
c#sqlvarbinarybinaryformatterserializationbinder

Data object update using SerializationBinder and Surrogate


I am getting a Object of type 'Project1.Class1[]' cannot be converted to type 'Project2.Class1[]'.' when trying to take the data from class1 project 1 to project 2

The object getting passed is a List of Project1.Class1 that contains a subobject of Class2. So I created two Surrogate classes to handle converting the objects but I am getting that error before the surrogates handle updating the List of Class1.

[Serializable]
internal class Class1Upgrader
{
   public BinaryFormatter CreateFormatter()
   {
      BinaryFormatter binaryFormatter = new BinaryFormatter();
      SurrogateSelector selector = new SurrogateSelector();
      selector.AddSurrogate(typeof(Project1.Class1), new StreamingContext(StreamingContextStates.All), new Class1Surrogate());
      selector.AddSurrogate(typeof(Project1.Class2), new StreamingContext(StreamingContextStates.All), new Class2Surrogate());
      binaryFormatter.SurrogateSelector = selector;
      binaryFormatter.Binder = new Class1Binder();

      return binaryFormatter;
   }
}

[Serializable]
internal class Class1Binder : SerializationBinder
{
  public override Type BindToType(string assemblyName, string typeName)
  {
     if (typeName == typeof(List<Project1.Class1>).FullName)
     {
        return typeof(List<Project2.Class1>);
     }
     return null;
  }
}

Edit: After fixing the issue this solution will be a huge help for people that need to update varbinary fields in SQL within C#. Most of the code roots are in the answer to get people started. :)


Solution

  • [Serializable]
    internal class Class1Upgrader
    {
      public BinaryFormatter CreateFormatter()
      {
         BinaryFormatter binaryFormatter = new BinaryFormatter();
         SurrogateSelector selector = new SurrogateSelector();
         selector.AddSurrogate(typeof(Project2.Class1), new StreamingContext(StreamingContextStates.All), new Class1Surrogate());
         selector.AddSurrogate(typeof(Project1.Class2), new StreamingContext(StreamingContextStates.All), new Class2Surrogate());
         binaryFormatter.SurrogateSelector = selector;
         binaryFormatter.Binder = new Class1Binder();
         return binaryFormatter;
      }
    }
    
    [Serializable]
    internal class Class1Binder : SerializationBinder
    {
      public override Type BindToType(string assemblyName, string typeName)
      {
        if (typeName.Contains("System.Collections.Generic.List`1[[Project1.Class1, Class1"))
        {
           return typeof(List<Project2.Class1>);
        }
        if (typeName == typeof(Project1.Class1).FullName)
        {
           return typeof(Project2.Class1);
        }
        return null;
      }
    }
    
    internal class Class1Surrogate : ISerializationSurrogate
    {
       public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
       {
         Project2.Class1 result = (Project2.Class1)obj;
         //Handle the logic here to convert your old properties into your new one
         return result;
       }
       public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
       {
         throw new NotImplementedException();
       }
    }
    
    internal class Class2Surrogate : ISerializationSurrogate
    {
       public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
       {
         Project2.Class1 result = new Project2.Class1();
         //Handle the logic here to convert your old properties into your new one
         return result;
       }
       public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
       {
         throw new NotImplementedException();
       }
    }
    

    So the solution is to change your binder to return the new object and not the old to be passed through the surrogate. This is important when working with List of a object that you are trying to upgrade. So Adding typeof(Project2.Class1) the surrogate class will handle converting your properties. So in your SetObjectData in the surrogate in object will be typeof(Project2.Class1) like shown above in the Class1Surrogate.