11

Recently, we moved a part of our code to different project library.

Unfortunately, it appears that those data have been serialized into the database with a BinaryFormatter(don't ask me why, I don't know and I hate this idea).

Now I'm responsible to create an update tool that update the database(The tool is launched automatically by our software when it detects a database that need updates, based on version):

  1. Create new columns
  2. Deserialize the binary column
  3. Write the deserialized columns into the new column
  4. Delete the old binary columns

My problem is that when I try to deserialize, it tells me that :

Unable to find assembly 'MyOldAssemblyName, Version=2.0.0.0, Culture=neutral, PublicKeyToken=a5b9cb7043cc16da'.

But this assembly doesn't exists anymore. I've no issue to put this class in my "updater" project, but no way that I can keep this old project only to contains this file.

Is there a way to specify to the BinaryFormatter that it has to deserialize the Stream it receives with a specified class?

Or say that the assembly has been renamed, or ???

J4N
  • 19,480
  • 39
  • 187
  • 340

3 Answers3

8

To tell it that the type has moved between assemblies (but that it retains the old name and namespace), you can sometimes use (in the old assembly) [assembly:TypeForwardedTo(typeof(TheType))]. The "sometimes" here is because you need to use typeof, which means you need to have a reference from the old assembly to the new assembly, which is not always possible - but often is (especially if you are moving a type from a UI layer down to a POCO/DTO layer, since the UI usually references to POCO/DTO).

However, if you have renamed the type of changed namespace, it would require you to write a custom "binder" (see here).

It should be noted that BinaryFormatter is inherently a type-based serializer, and you will always get a lot of issues when versioning or refactoring the code. If the type is not "write once, then never ever change it", then I would strongly suggest using something more flexible - somethat that is contract based rather than type based. Basically, just about anything other than BinaryFormatter (or NetDataContractSerializer): any of XmlSerializer, DataContractSerializer, protobuf-net, Json.NET, etc will all be fine and won't care that you relocated or renamed a type.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Like I said, the old assembly doesn't exists anymore. And like I said, it's not my choice to use a binary formatter, and I'm writing it now in separate columns. – J4N Oct 21 '13 at 09:24
  • Excuse me, I didn't saw your binder part. I will validate this answer as soon as I did check the results – J4N Oct 21 '13 at 09:26
  • @J4N I've seen assemblies before that contain **nothing** except `[assembly:TypeForwardedTo(...)]` - that isn't uncommon. But in your case, it sounds like you'll need to use a custom "binder" then, as per the link in my answer. – Marc Gravell Oct 21 '13 at 09:26
5

In fact I think I found myself the solution.

We can give a SerializationBinder to the binary formater, which will allows us to resolve manually a class we found in the stream.

More informations here

J4N
  • 19,480
  • 39
  • 187
  • 340
0

I recently ran into this issue myself and would like to post how I managed to get around this. Serialize the object per normal the key to everything is when the binaryformatter is deserializing you can use a Binder. With the binder below I simplified MSDN's solution all you need to do is hand back the versioning of the assembly your using along with the class name.

    public object DeserializeObject()
    {
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        binaryFormatter.Binder = new VersionDeserializer();

        using (MemoryStream memoryStream = new MemoryStream())
        {
            try
            {
                memoryStream.Write(_data, _ptr, count);
                memoryStream.Seek(0, SeekOrigin.Begin);
                return binaryFormatter.Deserialize(memoryStream);
            }
            catch (Exception e)
            {
                return null;
            }
        }
    }

    sealed class VersionDeserializer: SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            Type deserializeType = null;
            String thisAssembly = Assembly.GetExecutingAssembly().FullName;
            deserializeType = Type.GetType(String.Format("{0}, {1}",
                typeName, thisAssembly));

            return deserializeType;
        }
    }
levon
  • 1