System.Runtime.Serialization.SerializationException: Unable to find assembly MyAssembly
Asked Answered
C

9

14

So I've found a bunch of threads on this topic but I don't think I've found one that applies yet.

Basically my .exe loads a .dll (MyAssembly) file which does the serialization and loading. Obviously it serializes quite fine.

But when I go to deserialize the file within the MyAssembly.dll file it explodes with the error in the title of this post.

Anyone have any ideas? I don't understand how it can't find the assembly that is calling the code!

My code:

// deserialize


 using (var target = new System.IO.FileStream(Path, System.IO.FileMode.OpenOrCreate))
 {
     var bin = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
     var Obj = bin.Deserialize(target);
     if (Obj != null)
     {
         ObjectToStore = (ObjectTypeInMyAssembly)Obj;
     }
 }

// serialize
 using (var target = new System.IO.FileStream(Path, System.IO.FileMode.OpenOrCreate))
 {
     var bin = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
     bin.Serialize(target, ObjectToStore);
 }
Courtland answered 20/2, 2013 at 22:15 Comment(3)
Well how about showing the code where it blows up and the structure of the Serialization code as well if you can..Scheldt
Use fuslogvw.exe to troubleshoot assembly resolution problems.Tompion
take a look at this ...msdn.microsoft.com/en-us/library/…Scheldt
O
13

Is the DLL in the same folder like the EXE?
I see you serialize/deserialize a object that lives in the DLL ("MyAssembly"). When deserialize, the formatter determines the name of the type from serialized data, and tries to find this type in the assembly at the main executable folder, that is- EXE folder.
Solution- move the DLL to EXE folder. There is a way to cause the formatter to search in another assembly, capture the event AppDomain.AssemblyResolve and return your DLL. See MSDN.

Odontograph answered 21/2, 2013 at 1:52 Comment(1)
Cool. Was looking for a solution and tried lots of them. None worked but this. :) Thanks a lotBubbly
L
7

Well, I used a trick that worked!

sealed class CustomizedBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        Type returntype = null;
        string sharedAssemblyName = "SharedAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
        assemblyName = Assembly.GetExecutingAssembly().FullName;
        typeName = typeName.Replace(sharedAssemblyName, assemblyName);
        returntype =
                Type.GetType(String.Format("{0}, {1}",
                typeName, assemblyName));

        return returntype;
    }

    public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        base.BindToName(serializedType, out assemblyName, out typeName);
        assemblyName = "SharedAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
    }
}

use the binder for binary formatters, like this:

BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new CustomizedBinder();
Lucindalucine answered 29/5, 2014 at 17:51 Comment(0)
N
3

I'd like to build on Sean Ed-Man's answer, which is good, but doesn't work in my case.

If you can instantiate the class but the BinaryFormatter can't resolve it, this may work for you.

In my case, the calling assembly (PluginAssembly for this example) is being run as a plugin from the executable, in the form of a zip file. For some reason I can directly resolve the class (from NeededAssembly) when instantiating, but the BinaryFormatter can't resolve it. NeededAssembly is, of course, included as a reference to the PluginAssembly project, which is why I can instantiate. I don't know why the BinaryFormatter is different.

Regardless, this is what worked for me:

public class PluginAssembly
{
    // (class code here)

    private sealed class CustomizedBinder : SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            Type returntype = null;
            if (typeName.StartsWith("NeededAssembly.RemoteClient.MessagePayload"))
            {
                returntype = typeof(MessagePayload);
            }
            else if (typeName.StartsWith("NeededAssembly.RemoteClient.ResultsPayload"))
            {
                returntype = typeof(ResultsPayload);
            }
            else if (typeName.Equals("System.Collections.Generic.List`1[[NeededAssembly.ShortResult, NeededAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"))
            {
                returntype = typeof(List<ShortResult>);
            }
            else
            {
                returntype =
                        Type.GetType(String.Format("{0}, {1}",
                        typeName, assemblyName));
            }
            return returntype;
        }

        public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
        {
            base.BindToName(serializedType, out assemblyName, out typeName);
            if (serializedType.ToString().Contains("NeededAssembly"))
            {
                assemblyName = typeof(MessagePayload).Assembly.FullName;
            }
        }
    }
}

Of course, don't forget to use it:

BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new CustomizedBinder();

Basically I simply get a typeof for the needed class, which works.

Nosewheel answered 23/7, 2015 at 19:28 Comment(0)
L
1

I had a problem with this exception. I am writing a WPF plugin for a third party application. The application loads my assembly which should deserialize a type from another dll, MyType for example, which is in a private assembly added as a reference to my plugin and in the same directory as the plugin dll.

What seems strange to me was that I can instantiate MyType in the plugin, but it throws this exception when deserializing in the same class.

The solution for me was suggested by RoadBump and is very straightforward, but I don't understand why the surrounding code can find the assembly (if it can't this method will not work) but the deserialization call in the same code, can't.

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(MyTypeResolveEventHandler);

private Assembly MyTypeResolveEventHandler(object sender, ResolveEventArgs args)
{
    return typeof(MyType).Assembly;
}
Lammers answered 5/4, 2019 at 13:52 Comment(0)
L
0

What I see in your code with both your serialize and deserialize is the use of the System.IO.Path class instead of the actual path to the file. I usually execute this as follows:

    static void Main(string[] args)
    {
        string filepath = @"C:\FileLocation\";
        string filename = "datasaved.bin";
        MyClass something = new MyClass();

        // serialize
        using ( FileStream strm = File.OpenWrite(Path.Combine(filepath, filename)))
        {
            BinaryFormatter ser = new BinaryFormatter();
            ser.Serialize(strm, something);
        }

        // deserialize
        using (FileStream strm = File.OpenRead(Path.Combine(filepath, filename)))
        {
            BinaryFormatter ser = new BinaryFormatter();
            something = ser.Deserialize(strm) as MyClass;
        }
    }

Note two things: doing the Deserialize as will handle the object being null or not the expected type. Also I don't abuse the use of var.

Lodestone answered 20/2, 2013 at 23:58 Comment(0)
L
0

I would check if Marc Gravell's answer here applies... in short "One option would be to move the type to a library dll that both the other projects reference - then it is only defined once, and it will be happy"

In my case I had temporarily created a copy of the class that I intended to serialize (in the assembly doing the serializing) early in development, but forgot about it when deserializing! So the deserializer never did have access to the same class that did the serialising!

It is also worth checking out the properties of the project containing the class being serialized - Check for typos in its assembly name. You might want to try flattening out your namespace hierachy too to see if that helps.

Lundy answered 14/8, 2017 at 11:3 Comment(0)
S
0

There is yet another solution to this problem, which is to list the folder containing the assembly in App.config. You do this by adding a probing element, like this:

<configuration>  
   <runtime>  
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">  
         <probing privatePath="bin;bin2\subbin;bin3"/>  
      </assemblyBinding>  
   </runtime>  
</configuration> 
Stillman answered 21/8, 2017 at 7:58 Comment(0)
A
0

I had a specific case of the SerializationException described by the OP, and found a relatively simple solution. In my case, I am implementing a DeepClone extension method using the BinaryFormatter technique. My application also makes use of plug-ins, assemblies that I load programmatically. I encountered the SerializationException when the BinaryFormatter.Deserialize method was called for an object whose class is contained in a plug-in.

I find this very strange, because, by definition, the containing assembly is loaded into my AppDomain. (I couldn't call an extension method on the object if it wasn't!) Apparently, the BinaryFormatter does not check the AppDomain to resolve assemblies.

Many of the solutions suggested here for solving this problem seem to involve hard coding the name of the assembly, or class, into a custom SerializationBinder. This is not useful, as I want my DeepClone method to be as generic as possible. My answer to this problem (shown below) hooks the AssemblyResolve event in the current AppDomain and then searches the AppDomain (using LINQ) for the requested assembly. No hard coded assembly or class names required.

Here is the code:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;

namespace StackOverflowDemo
{
    public static class Extend
    {
        /// <summary>
        /// Static initializer is used to register the Resolve Event Handler
        /// </summary>
        static Extend()
        {
            AppDomain.CurrentDomain.AssemblyResolve += ResolveEventHandler;
        }

        /// <summary>
        /// This resolver will find any Assembly that is already loaded into
        /// the current AppDomain and return it.
        /// <para/>
        /// You would think this would not be necessary, but the 
        /// BinaryFormatter.Deserialize method apparently can not 
        /// find an assembly that has been loaded programmatically
        /// as a plug-in, and whose DLL does not reside in the same
        /// folder as the executable file.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        private static Assembly ResolveEventHandler( object sender, ResolveEventArgs args )
        {
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
            Assembly result = (from a in assemblies where args.Name.Equals(a.FullName) select a).FirstOrDefault();
            return result;
        }


        /// <summary>
        /// Slow, and requires that the source be marked as [Serializable], but 
        /// clones *everything* and is not complicated.
        /// <para/>
        /// Note that by itself, this method will fail if an attempt is made to
        /// create a deep copy of an object whose class is contained in an assembly
        /// that was loaded programmatically (i.e., loaded as a plug-in).
        /// <para/>
        /// See https://stackoverflow.com/a/1213649
        /// <para/>
        /// and https://stackoverflow.com/a/23017515
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="src"></param>
        /// <returns></returns>
        public static T DeepClone<T>( this T src )
        {
            if (!typeof(T).IsSerializable)
            {
                throw new ArgumentException(string.Format("[Extend.DeepClone] Type '{0}' is not Serializable", typeof(T).Name));
            }

            if (Object.ReferenceEquals(src, null))
            {
                return default(T);
            }

            using (Stream stream = new MemoryStream())
            {
                IFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, src);
                stream.Seek(0, SeekOrigin.Begin);
                return (T)formatter.Deserialize(stream);
            }
        }
    }
}
Arelus answered 14/2, 2019 at 15:36 Comment(0)
B
0

I just stumbled over the same issue and figured out that my fault was the existance of two versions of that assembly within my probing paths. That typically happens if you first did your assembly and afterwards fix a postbuild event to move it to where you really need it.

Botulin answered 4/9, 2020 at 14:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.