銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

Assembly.LoadFrom するDLLの中でAssemblyBinding したくて困った話

VSTS拡張の話はまだ続きますが、今日困ったことが解決したのでネタにしました。

Plugin的な機構を実現したくて、Assembly.LoadFromでDLLを読み込んでいる箇所があるのですが、読み込んだDLLの先で呼び出しているライブラリ(実際にはNewtonsoft.Json)のバージョンが本体より古いため、AssemblyBindingを設定する必要がありました。 Web.config に指定しても働いてくれなくてなんでだと思っていたらこの記事を見つけました。

Redirecting Assembly Loads at Runtime – SLaks.Blog

この記事にすべて書いてあるのですが、AppDomain.CurrentDomain.AssemblyResolveイベントハンドラを記述することで、ハンドラの中で読み込む対象のDLLを変更することができる、というものになります。今回使うことになった、Newtonsoft.Jsonの場合のコードがこうなります。

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
    //本体が参照しているNewtonsoft.Jsonのバージョンを指定する
    var targetVersion = new Version(7, 0, 0, 0);
    var publicKeyToken = "30ad4fe6b2a6aeed";

    var requestedAssembly = new AssemblyName(args.Name);
    if (requestedAssembly.Name != "Newtonsoft.Json")
        return null;

    Debug.WriteLine("Redirecting assembly load of " + args.Name
                  + ",\tloaded by " + (args.RequestingAssembly?.FullName ?? "(unknown)"));

    requestedAssembly.Version = targetVersion;
    requestedAssembly.SetPublicKeyToken(new AssemblyName("x, PublicKeyToken=" + publicKeyToken).GetPublicKeyToken());
    requestedAssembly.CultureInfo = CultureInfo.InvariantCulture;

    return Assembly.Load(requestedAssembly);
};

Newtonsoft.Jsonが読み込もうとされたら、バージョン固定でリダイレクトしています。当然本体が参照しているバージョンと違うと無限Loadが試みられるので注意が必要です*1。また、当然本体が参照するバージョンが替われば書き換える必要があります。実運用考えるともう少し工夫の余地はありますが、とりあえず回避策ということで紹介しました。

*1:そのため参照先のサイトではイベントハンドラを解除している