英語で読む

次の方法で共有


OnAssemblyResolve ハンドラーでのシリアル化によって再帰が発生する可能性がある

この記事は、シリアル化によってアセンブリ リゾルバー ハンドラーが再帰的に呼び出される問題を解決するのに役立ちます。

元の製品バージョン: .NET Framework 3.5 Service Pack 1、4.5
元の KB 番号: 2756498

現象

次の例のように、 XmlSerializerAssembly 属性でマークされたクラスがあるとします。

[Serializable, XmlRoot(ElementName="Bindings", IsNullable=false), XmlSerializerAssembly(AssemblyName="Contoso.ObjectLoaders.Bindings")]
public class Bindings
{
    (...)
}

生成されたアセンブリは、 bin フォルダーではなく、署名され、グローバル アセンブリ キャッシュ (GAC) に配置されます。 また、 AppDomain.AssemblyResolve イベントのハンドラーを実装し、アセンブリ リゾルバー ハンドラー メソッドでクラスのインスタンスを次のようにシリアル化します。

private static Assembly ResolveAssemblies(object sender, ResolveEventArgs args)
{
    // Some code here
    var serializer = new XmlSerializer(typeof(Bindings));
    // Some more code here
}

このシナリオでは、シリアル化によってアセンブリ リゾルバー ハンドラーが再帰的に呼び出され、スタック オーバーフローが発生する可能性があります。

原因

この動作は仕様です。 XmlSerializerAssembly属性を持つアセンブリは、Assembly.LoadWithPartialName(string)と共に読み込まれ、OnResolveAssembly ハンドラーが再度呼び出されます。

回避策 1: XmlSerializerAssembly 属性を削除する

このメソッドは、アセンブリが Assembly.LoadWithPartialName(string)によって読み込まれないようにします。

回避策 2: より堅牢なアセンブリ解決ハンドラーを記述する

これは推奨される回避策であり、再帰に関連するすべての問題を解決します。 次の例は、この回避策の単純なコード テンプレートです。 主な考え方は、既に解決済みのアセンブリを汎用リストに追加し (スレッド セーフであるために同時実行バッグが選択されました)、アセンブリが既に解決中の場合は返します。

static ConcurrentBag<string> listOfAssemblies = new ConcurrentBag<string>();
private static Assembly ResolveAssemblies(object sender, ResolveEventArgs args)
{
    if (listOfAssemblies.Contains(args.Name))
    {
        // Already resolving this assembly, return now
        return null;
    }
    try
    {
        listOfAssemblies.Add(args.Name);
        // Add your handler code here
    }
    finally
    {
        // Assembly was handled, remove from list
        listOfAssemblies.Remove(args.Name);
    }
}

関連情報