web-dev-qa-db-ja.com

同じdllの2つの異なるバージョンを使用していますか?

私は2つのプリコンパイルされたDLLを与えられました:

Common.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL

Common.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL

APIの違いの例:

API

そして、私は両方を単一のプロジェクトにロードして、次のようなことをしようとしています:

extern alias v10;
extern alias v20;

private static void UpgradeUser()
{
    // Load old user
    var userOld = new v10::Common.Data.UserData();
    userOld.loadData("user.dat");

    // Create new user
    var userNew = new v20::Common.Data.UserData();

    // Copy properties  
    userNew.FirstName = userOld._firstName;
    userNew.LastName = userOld._lastName;
    userNew.Age = userOld._age;

    // Invoke method from v10 and v20 API
    userOld.version();
    userNew.DisplayVersion();

    if (userNew.GetUserInfo() != userOld.getInfo())
    {
        throw new Exception("Discrepencies in upgrade ");
    }

    Console.WriteLine("Upgrade done!");
}

プロジェクト参照とapp.configを次のように設定しました。また、dllを手動で出力フォルダーにコピーし、appconfig hrefと一致させています

<!-- [Edited: see history for previous version] -->
<Reference Include="Common.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL">
  <HintPath>libs\Common.Data.1_0_0_0.dll</HintPath>
  <Aliases>v10</Aliases>
</Reference>

<Reference Include="Common.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL">
  <HintPath>libs\Common.Data.2_0_0_0.dll</HintPath>
  <Aliases>v20</Aliases>
</Reference>

<runtime>
  <assemblyBinding xmlns="urn:schemas-Microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="Common.Data" publicKeyToken="f3b12eb6de839f43" culture="neutral" />
      <codeBase version="1.0.0.0" href="libs\Common.Data.1_0_0_0.dll" />
      <codeBase version="2.0.0.0" href="libs\Common.Data.2_0_0_0.dll" />
    </dependentAssembly>
  </assemblyBinding>
</runtime>

ビルドが成功するまでになりました。

しかし、実行しようとすると、UserData.loadDataMissingMethodExceptionが返されます。

Error

私は複数のstackoverflowの投稿、msdn、codeprojectの記事を読んできましたが、機能するようには思えません。

リンク1リンク2リンクリンク4

私は重要なステップを逃していると思いますが、何を理解できないか、本当に助けを使うことができます。

[Edit1]

Dllを個別に使用してみましたが、動作します。 (混乱を解消。スクリーンショットのバージョン履歴を参照)

[Edit2]

Mukesh Kumarの提案を試し、app.configを次のように変更しました。

<runtime>
  <assemblyBinding xmlns="urn:schemas-Microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="Common.Data" 
                        publicKeyToken="f3b12eb6de839f43" 
                        culture="neutral" />
      <bindingRedirect oldVersion="1.0.0.0"
                       newVersion="2.0.0.0"/>
    </dependentAssembly>
  </assemblyBinding>
</runtime>

しかし、まだ機能しません。 FileNotFoundExceptionを取得しています。

[Edit3]

OK、<bindingRedirect>が正しくないことはほぼ確実です。代わりに<codeBase>にする必要があります。

CLIからコンパイルしてみました:

csc Program.cs /reference:v10=libs/Common.Data.1_0_0_0.dll /reference:v20=libs/Common.Data.2_0_0_0.dll

そしてそれはうまく機能します。両方のAPIを同時に使用できました。

enter image description here

しかし、Visual Studioからビルドしようとすると、既に参照エイリアスを指定しているにもかかわらず、/ referenceについてエラーが発生します。

The extern alias 'v10' was not specified in a /reference option

<Reference />を変更して、<SpecificVersion>True</SpecificVersion>を含めたり除外したりしても、何の効果もありません。

私はこれを見つけました post 、解決策はパスを削除して再追加することでした。そのソリューションを使用すると、Visual Studioは正常にビルドされますが、System.MissingMethodExceptionに戻ります。

私はそれをほとんど手に入れたように感じますが、完全ではありません。 Visual Studioを正しくビルドするにはどうすればよいですか?

[Edit4]

Miguelの方法を試しましたが、まだ動作しません。

この試みでは、dllの名前を元の名前に戻し、代わりに別のフォルダーに保存しました。

enter image description here

その後、app.configを更新してbindingRedirectとコードベースを実行しました。

実行すると、MissingMethodExceptionが表示されます。

4- Find the and put in Falseの意味がわからなかったので、<Private><SpecificVersion>のすべての組み合わせ、および<Reference Include>をFQNに設定しようとしましたが、それらの組み合わせは機能しませんでした。

3回目の編集を見て、CLI経由でサンプルをコンパイルし、正常に(名前を変更したdll + app.config codebase hrefを使用して)実行できました。

私の問題は、csprojをどのように構成すればよいのかということです。

21
jayars

BindingRedirectでdependentAssemblyを使用する必要がありますが、別のフォルダーにdllを配置するか、別の名前で保存する必要もあります。これが完了したら、app.configに以下を追加する必要があります。

<assemblyBinding xmlns="urn:schemas-Microsoft-com:asm.v1">
  <dependentAssembly>
        <assemblyIdentity name="myAssembly"
                          publicKeyToken="here token dll"
                          culture="neutral" />
       <bindingRedirect oldVersion="0.0.0.0-1.0.0.0" newVersion="1.0.0.0" />
       <bindingRedirect oldVersion="1.0.0.1-2.0.0.0" newVersion="2.0.0.0" />
       <codeBase version="1.0.0.0" href="folder\namedll.dll" />
       <codeBase version="2.0.0.0" href="folder\namedll.dll" />
      </dependentAssembly>
   </assemblyBinding>

このコードではコンパイルして実行する必要がありますが、VSはコンパイル時にapp.configのコードを削除または上書きする場合があります。編集フォルダーの設定ファイルで確認する必要があります。これが成功した場合、.csprojを編集できます。そのためには、次のことを行う必要があります。

1-影響を受けるプロジェクトをアンロードします
2-プロジェクトを右クリック
3- [プロジェクトの編集]をクリックします
4- <AutoGenerateBindingRedirects>プロパティを見つけて、Falseに設定します
5-変更を保存してプロジェクトをリロードする

これは私のために動作します。私のプロジェクトでは、Automapperの2つのバージョンを使用しています。

最後に、別の解決策は、AppDomain.CurrentDomain.AssemblyResolveビルドイベントを使用して特定のdllをロードすることです。

そのためには、イベントをキャッチする必要があります。

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

public static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    //debug and check the name
    if (args.Name == "MyDllName")
        return Assembly.LoadFrom("c:\\pathdll\midllv1.dll")
    else if(args.Name ="MyDllName2")
        return Assembly.LoadFrom("c:\\pathdll\midllv2.dll");
    else
        return Assembly.LoadFrom("");
}
10
miguel

私のニーズに合った完全な答えが見つからないので、タイプを解決するためにリフレクションを使用して、2つのdllを使用して関連するファイルをロードするAssemblyResolve方法でソリューションを寄付することにしました。このデモでは、MathFuncsという2つのdllを作成し、Add.add関数を呼び出そうとすると、2つの異なる実装で解決します。異なる結果を確認するには、バージョン整数を値1と2の間で切り替えます。

public static int version = 1;
    public static void Main(string[] args)
    {
        AppDomain.CurrentDomain.AssemblyResolve += Program.CurrentDomain_AssemblyResolve;
        version = 1;
        Type a = Type.GetType("MathFuncs.Add, MathFuncs, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
        MethodInfo methodInfo = a?.GetMethod("add");
        object result = null;
        if (methodInfo != null)
        {
            object[] parametersArray = new object[] {1, 2};
            result = methodInfo.Invoke(Activator.CreateInstance(a, null), parametersArray);
        }

        if (result != null)
        {
            Console.WriteLine((int)result);
        }
        else
        {
            Console.WriteLine("failed");
        }

        Console.Read();
    }


    public static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        FileInfo fi = null;
        if (version == 1)
        {
            fi = new FileInfo("C:\\Users\\ohbitton\\Desktop\\MathFuncs\\MathFuncs.dll");
        }
        else
        {
            fi = new FileInfo("C:\\Users\\ohbitton\\Desktop\\MathFuncs2\\MathFuncs.dll");
        }
        return Assembly.LoadFrom(fi.FullName);

    }

Type.GetTypeの名前空間の完全な名前を取得するには、powershellを使用できます。

([system.reflection.Assembly]::loadfile("C:\Users\ohbitton\Desktop\MathFuncs\MathFuncs.dll")).FullName
1
Ohad Bitton

次のように設定の詳細を入力してください。

 <dependentAssembly>
            <assemblyIdentity name="myAssembly"
                              publicKeyToken="32ab4ba45e0a69a1"
                              culture="neutral" />
            <bindingRedirect oldVersion="1.0.0.0"
                             newVersion="2.0.0.0"/>
  </dependentAssembly>

ここでは、dllの古いバージョンと新しいバージョンを提供できます。この 記事this に従うだけです。

これがお役に立てば幸いです。

1
Mukesh Kumar