使用动态加载的 .Net 程序集进行二进制序列化

Bau*_*aud 9 .net c# serialization .net-assembly

我将我的类的一个实例序列化为一个文件(带有BinaryFormatter

之后,在另一个项目中,我想反序列化这个文件,但没有成功,因为我的新项目没有我旧类的描述。在.Deserialize()收到一个异常

Unable to find assembly '*MyAssembly, Version=1.9.0.0, Culture=neutral, PublicKeyToken=null'.*".
Run Code Online (Sandbox Code Playgroud)

但是我有包含我想要反序列化的旧类的描述的程序集的 .DLL。

我不想在项目中添加对 this DLL 的引用(我希望能够反序列化任何类型程序集的类......)

如何通知序列化器/反序列化器使用我动态加载的程序集?

dbc*_*dbc 5

假设您是通过Assembly.Load()或加载程序集Assembly.LoadFrom(),那么正如Chris Shain对动态加载类型SerializationException 的回答中所述,您可以在反序列化期间使用该事件加载动态程序集。但是,出于安全原因,您将希望防止加载完全意外的程序集。AppDomain.AssemblyResolve

一种可能的实现是引入以下内容:

public class AssemblyResolver
{
    readonly string assemblyFullPath;
    readonly AssemblyName assemblyName;

    public AssemblyResolver(string assemblyName, string assemblyFullPath)
    {
        // You might want to validate here that assemblyPath really is an absolute not relative path.
        // See e.g. /sf/ask/389552061/
        this.assemblyFullPath = assemblyFullPath;
        this.assemblyName = new AssemblyName(assemblyName);
    }

    public ResolveEventHandler AssemblyResolve
    {
        get
        {
            return (o, a) =>
                {
                    var name = new AssemblyName(a.Name);
                    if (name.Name == assemblyName.Name) // Check only the name if you want to ignore version.  Otherwise you can just check string equality.
                        return Assembly.LoadFrom(assemblyFullPath);
                    return null;
                };
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,在启动的某个地方,添加一个合适的ResolveEventHandlerto AppDomain.CurrentDomain.AssemblyResolveeg 如下:

class Program
{
    const string assemblyFullPath = @"C:\Full-path-to-my-assembly\MyAssembly.dll";
    const string assemblyName = @"MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";

    static Program()
    {
        AppDomain.CurrentDomain.AssemblyResolve += new AssemblyResolver(assemblyName, assemblyFullPath).AssemblyResolve;
    }
Run Code Online (Sandbox Code Playgroud)

这会ResolveEventHandler检查请求的程序集是否具有您的动态程序集的名称,如果是,则从预期的完整路径加载当前版本。

另一种方法是编写自定义SerializationBinder并将其附加到BinaryFormatter.Binder. 在BindToType (string assemblyName, string typeName)绑定器中需要检查属于您的动态程序集的类型,并适当地绑定到它们。这里的技巧是处理动态加载的类型嵌套在来自另一个程序集的泛型中的情况,例如List<MyClass>. 在这种情况下,assemblyName将是List<T>not程序集的名称MyClass。有关如何执行此操作的详细信息,请参阅

@sgnsajgon询问的评论中我想知道为什么我不能像在项目中显式引用签名程序集时那样反序列化流 - 仅此而已。formatter.Deserialize(stream)

虽然我不知道 Microsoft 员工在设计这些类(回到.Net 1.1 中)时在想什么,但可能是因为:

顺便说一句,什么是内置的BinaryFormatter基于.NET的序列化的不足之处?对您在使用BinaryFormatter.


Han*_*ant 3

二进制序列化对于 DLL Hell 有着严肃的态度。它记录了数据序列化时包含该类型的确切程序集。并坚持在反序列化数据时找到确切的程序集。确保序列化数据与类型匹配的唯一方法是,采取任何捷径只会确保您在幸运时得到异常,而在不幸运时得到垃圾数据。这种情况迟早发生的可能性是 100%。

因此,您需要完全放弃可以使用“动态加载程序集”并让它“反序列化任何类型的类”的想法,这是一种幻觉。您可以旋转命运之轮并<bindingRedirect>在 app.exe.config 文件中添加一个 来强制 CLR 使用不同的程序集版本。处理不幸事件现在是你的责任。许多程序员抓住了这个机会,但很少有人在没有吸取新教训的情况下从经验中回来。必须这样做才能了解后果。所以请继续。

  • @sgnsajgon - 您可以使用[AppDomain.AssemblyResolve](https://msdn.microsoft.com/en-us/library/system.appdomain. assemblyresolve.aspx),如[动态加载类型的SerializationException](https:/ /stackoverflow.com/a/9162553/3744182)。我认为自定义的 `SerializationBinder` 也应该可以工作,请参阅 [BinaryFormatter deserialize gets SerializationException](/sf/ask/148403881/) 和 [How to create a SerializationBinder for the Binary Formatter thathands the moving of types从一个程序集和命名空间到另一个](/sf/answers/1394923911/)。 (3认同)
  • 我检查用于序列化实例的程序集是否与动态加载到反序列化的程序集相同。这些程序集看起来相同:相同的名称/版本/区域性/GUID。当我浏览这些 Reflection.Assemly 时,发现差异: -locations -filename (renamed dll) -evidences -# of class 这些程序集并不完全相同(我知道这一点),但具有相同的序列化标识和描述class : - 类的数量应该不是问题,因为只有一个类标记为 [Serialized] - 该类具有相同的可序列化字段描述。 (2认同)

归档时间:

查看次数:

1388 次

最近记录:

4 年,10 月 前