13

私は得ています:

System.Runtime.Serialization.SerializationException: アセンブリが見つかりません 'myNameSpace、バージョン = 1.0.0.0、カルチャ = ニュートラル、PublicKeyToken = null

シリアル化したプログラムとは別のプログラムでデータを逆シリアル化しようとしたとき。

グーグルで調べたところ、明らかにこれは共有アセンブリを使用してのみ実行できることがわかりました。

しかし、私のデータベースはこのシリアル化されたオブジェクトでいっぱいで、それらを取り出すユーティリティ プログラムが必要です。この動作をオーバーライドして、まったく同じクラスをフィードし、強制的に逆シリアル化する方法はありますか?


このスニペットは既に見つけましたが、これをどこにどのように配置/使用すればよいかわかりません。

   static constructor() {
        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
   }

    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
        Assembly ayResult = null;
        string sShortAssemblyName = args.Name.Split(',')[0];
         Assembly[] ayAssemblies = AppDomain.CurrentDomain.GetAssemblies();
         foreach (Assembly ayAssembly in ayAssemblies) {
            if (sShortAssemblyName == ayAssembly.FullName.Split(',')[0]) {
                 ayResult = ayAssembly;
                 break;
            }
         }
         return ayResult;
    }
4

6 に答える 6

13

オブジェクトを知っていれば、DLL を必要とせずにこの問題を回避できます...

http://spazzarama.com/2009/06/25/binary-deserialize-unable-to-find-assembly/

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.serializationbinder(VS.71).aspx

「System.Runtime.Serialization.SerializationBinder」クラスを使用します。このクラスから継承することにより、バイナリ フォーマッタからのすべてのタイプの要求を、選択したタイプにリダイレクトできます。

次に示すのは、最初にシリアル化されたストリームを作成したアセンブリのバージョンに関係なく、現在のアセンブリで型を検出できるようにするサンプルです。

sealed class AllowAllAssemblyVersionsDeserializationBinder : System.Runtime.Serialization.SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {     
        String currentAssembly = Assembly.GetExecutingAssembly().FullName;

        // In this case we are always using the current assembly
        assemblyName = currentAssembly;

        // Get the type using the typeName and assemblyName
        Type typeToDeserialize = Type.GetType(String.Format("{0}, {1}",
            typeName, assemblyName));

        return typeToDeserialize;
    }
}

public static MyRequestObject Deserialize(byte[] b)
{
    MyRequestObject mro = null;
    var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
    using (var ms = new System.IO.MemoryStream(b))
    {
       // To prevent errors serializing between version number differences (e.g. Version 1 serializes, and Version 2 deserializes)
       formatter.Binder = new AllowAllAssemblyVersionsDeserializationBinder();

       // Allow the exceptions to bubble up
       // System.ArgumentNullException
       // System.Runtime.Serialization.SerializationException
       // System.Security.SecurityException
       mro = (MyRequestObject)formatter.Deserialize(ms);
       ms.Close();
       return mro;
    }
}
于 2010-12-02T22:56:01.877 に答える
6

ユーティリティ プログラムがデシリアライズする方法を認識できるように、何らかの方法で元の型への参照を提供する必要があります。

簡単な方法は、型が最初に定義された DLL をユーティリティ プロジェクトへの参照として追加することです。

投稿したコードを使用すると、デシリアライザーが型を見つけられないと判断したときに、同じ DLL を動的にロードできます。これはより難しいアプローチですが (それほど難しくはありません)、どちらの場合も型を定義する DLL が必要になるため、参照を追加して静的にリンクするのがおそらく最も簡単です。

型が現在 DLL に含まれていない場合 (たとえば、EXE に含まれている場合)、クラスを EXE から新しい DLL に取り出し、元のプロジェクトと util プロジェクトの両方からその DLL を参照することをお勧めします。

于 2010-01-22T20:02:13.553 に答える
4

SerializationBinderカスタムがこの問題を処理する方法であるというJTtheGeekの答えは正しいです。ただし、その回答に示されているコード例は、私のユースケースには十分ではありません。次のことができるものが必要でした:

  1. バージョン番号と名前空間の不一致を処理します。
  2. 現在のアセンブリだけでなく、すべてのアセンブリを調べます。
  3. それでも、1 秒あたり 100 回呼び出すのに十分な速度が必要です。

これは私が思いついたものです:

using System;
using System.Collections.Generic;
using System.Linq;

namespace Company.Product.Common.Serialize
{
    /// <summary>
    /// A Serialization Binder that allows inexact matches (version number or namespace).
    /// </summary>
    public sealed class AllowInexactMatchSerializationBinder : System.Runtime.Serialization.SerializationBinder
    {
        static private Dictionary<string, Type> typeBindings = new Dictionary<string, Type>();

        /// <summary>
        /// When overridden in a derived class, controls the binding of a serialized object to a type.
        /// </summary>
        /// <param name="assemblyName">Specifies the <see cref="T:System.Reflection.Assembly" /> name of the serialized object.</param>
        /// <param name="typeName">Specifies the <see cref="T:System.Type" /> name of the serialized object.</param>
        /// <returns>
        /// The type of the object the formatter creates a new instance of.
        /// </returns>
        public override Type BindToType(string assemblyName, string typeName)
        {
            Type type;
            var assemblyQualifiedTypeName = String.Format("{0}, {1}", typeName, assemblyName);

            // use cached result if it exists
            if (typeBindings.TryGetValue(assemblyQualifiedTypeName, out type))
            {
                return type;
            }

            // try the fully qualified name
            try { type = Type.GetType(assemblyQualifiedTypeName); }
            catch { type = null; }

            if (type == null)
            {
                // allow any assembly version
                var assemblyNameWithoutVersion = assemblyName.Remove(assemblyName.IndexOf(','));
                var assemblyQualifiedTypeNameWithoutVersion = String.Format("{0}, {1}", typeName, assemblyNameWithoutVersion);
                try { type = Type.GetType(assemblyQualifiedTypeNameWithoutVersion); }
                catch { type = null; }
            }

            if (type == null)
            {
                // check all assemblies for type full name
                try
                {
                    type = AppDomain.CurrentDomain.GetAssemblies()
                        .SelectMany(a => a.ExportedTypes)
                        .Where(a => a.FullName == typeName)
                        .FirstOrDefault();
                }
                catch { type = null; }
            }

            if (type == null)
            {
                // check all assemblies for type name
                var name = typeName.Split('.').Last();
                try
                {
                    type = AppDomain.CurrentDomain.GetAssemblies()
                        .SelectMany(a => a.ExportedTypes)
                        .Where(a => a.Name == name)
                        .FirstOrDefault();
                }
                catch { type = null; }
            }

            typeBindings[assemblyQualifiedTypeName] = type;
            return type;
        }
    }
}
于 2014-08-20T19:01:27.697 に答える
4

同様の問題に遭遇し、次のリンクの助けを借りて動作するようになりました: BinaryFormatterDeserialize-not-finding-a-type

基本的に、デシリアライズする前に AssemblyResolve イベントをサブスクライブする必要があります。次に、デシリアライズ後に購読を解除します..

AppDomain.CurrentDomain.AssemblyResolve +=
                new ResolveEventHandler(CurrentDomain_AssemblyResolve);
// CODE TO DESERIALIZE HERE

AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(CurrentDomain_AssemblyResolve);

アセンブリを解決するために使用した方法は次のとおりです。

static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    try
    {
        if(args.Name == "MY ASSEMBLY NAME"))
        {
            //Load my Assembly 
            Assembly assem = Assembly.LoadFrom("MY ASSEMBLY PATH");
            if(assem != null)
                return assem;
        }
    }
    catch { ;}

    return Assembly.GetExecutingAssembly();
}
于 2011-12-02T15:44:23.397 に答える
1

データをシリアル化した元のアセンブリにアクセスできない場合は、SerializationBinder または SerializationSurrogate を使用できます。これら 2 つのインターフェイスを使用すると、逆シリアル化時に型が相互に変換される方法を制御できます。

于 2011-11-04T20:21:08.370 に答える
0

私はJTtheGeekが回答したソリューションに従いました。それを機能させるために、ステートメントの直前に次を追加する必要がありましたassemblyName = currentAssembly;

typeName = "yourNamespace.yourClassName";

その後、うまくいきました!

于 2013-11-27T11:12:49.673 に答える