2016-09-01 25 views
0

アセンブリの解決に問題があり、それが私を苦労させています。異なるバージョンのアセンブリを解決する

私の会社の製品は、多くのプラグインサービスを持つことができます。私のチームはこれらのサービスに取り組んでいます。ユーティリティアセンブリ(Utility.dll)には、再利用可能なコードがたくさんあります。

時々、私たちはUtility.dllに改善や新機能の追加を行っています。私たちは非常に厳しい下位互換性テストを行って、新しいバージョンのアセンブリが古いプラグインサービスすべてで動作することを確認します。

私が抱えている問題は、現在展開されているバージョンのUtility.dllを解決することです。

例を挙げておきます。

Utility.dllは現在、バージョン1.0.0.0

で我々は、その後Utility.dllを更新し、2.0にバージョンを更新Utility.dllバージョン1.0.0.0

を参照することにより構築されたサービスのサービスAを作成しています。 0.0

次に、Utility.dll 2.0.0.0を使用するサービスServiceBを作成します。

Utility.dllバージョン2.0.0.0は、ServiceAとServiceBの両方と互換性があります。

私はアプリケーションを起動します。展開されたバージョンのUtility.dllが読み込まれます。

私はServiceBを起動します。 AppDomain.CurrentDomain.AssemblyResolveイベントの実装が開始され、Utility.dll 2.0.0.0が返されます。

次に、ServiceAを起動します。 AppDomain.CurrentDomain.AssemblyResolveは決して解雇されませんが、私はUtility.dll 1.0.0.0のFileNotFoundExceptionを取得します。この時点で、私はUtility 2.0.0.0で解決したいと思います。バージョン1.0.0.0が見つからない場合、なぜAssemblyResolveイベントが発生しないのですか?

また、ServiceAを先に起動すると、AssemblyResolveが起動し、Utilty.dll 2.0.0.0がServiceA(これはUtilty.dll 1.0.0.0で作成されたもの)で解決されます。ただし、ServiceB(Utilty.dll 2.0.0.0でビルドされた)を起動すると、AssemblyResolveイベントが発生することはなく、Utility.dllバージョン1.0.0.0に対してFileNotFoundExceptionがスローされます。

何が起こっていますか?私は、現在展開されているバージョンのUtility.dllをすべてのサービスに使用したいだけです。

私はその後、私のサービスの各プラグインでこのようにそれを呼び出しています

public class UtilityLoader 
{ 
    private IServiceContext context; 

    public UtilityLoader(IServiceContext context) 
    { 
     this.context = context; 
     AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; 
    } 

    private bool Loaded 
    { 
     get 
     { 
      context.Application.Log.WriteInfo("Checking for Utility..."); 
      Assembly asmFound = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(asm => asm.FullName.Contains("Utility")); 
      context.Application.Log.WriteInfo(string.Format("Utility {0} loaded.{1}", asmFound == null ? "is not" : "is", asmFound == null ? string.Empty : string.Format(" Version: {0}", asmFound.GetName().Version))); 

      return asmFound != null; 
     } 
    } 

    public bool Load() 
    { 
     if (Loaded) 
      return true; 

     string utilityPath = Path.Combine(Session.DataDirectory, "Utility.dll"); 

     if (File.Exists(utilityPath)) 
     { 
      context.Application.Log.WriteInfo(string.Format("Utility.dll was found.")); 
      FileStream stream = File.OpenRead(utilityPath); 
      byte[] assemblyData = new byte[stream.Length]; 
      stream.Read(assemblyData, 0, assemblyData.Length); 
      stream.Close(); 

      try 
      { 
       Assembly.Load(assemblyData); 
      } 
      catch (Exception ex) 
      { 
       context.Application.Log.WriteInfo(string.Format("Could not load Utility: {0}", ex.Message)); 
       throw; 
      } 

      return true; 
     } 

     return false; 
    } 



    private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) 
    { 
     AssemblyName asmName = new AssemblyName(args.Name); 

     if (asmName.Name == "Utility") 
     { 
      context.Application.Log.WriteInfo("Resolving Utility"); 
      Assembly nuAsm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(asm => asm.FullName.Contains("Utility")); 
      context.Application.Log.WriteInfo(string.Format("Utility {0} already loaded.", nuAsm == null ? "is not" : "is")); 
      return nuAsm; 
     } 

     return null; 
    } 

} 

****** ******更新しました。

public void Execute(IServiceContext context, ServiceParameters serviceParams) 
{ 
    UtilityLoader loader = new UtilityLoader(context); 
    if (!loader.Load()) 
    { 
     MessageBox.Show("Utility not loaded.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 
     return; 
    } 

    StartService(context, serviceParams); 
} 

と呼ばれる負荷罰金だけでなく、ロードされた最初のサービスプラグインとしてUtility.dllの同じバージョンを参照して構築された任意の他のサービスのプラグインを取得する最初のサービスプラグイン。 Utility.dllの異なるバージョンで構築されたサービスプラグインが呼び出された場合、FileLoadExceptionがスローされます。ユーティリティ1.0.0で構築されたユーティリティ1.0.0.0 ServiceBで構築された

別の例

SERVICEA。0 ユーティリティ2.0.0.0で構築されたServiceB

ユーティリティ2.0.0.0がデプロイされています。

ユーザーが最初にServiceAを起動します。 Utility 2.0.0.0がロードされます。 AssemblyResolveイベントが発生します。アセンブリが解決されました。サービスが開始されます。

ユーザーは、同じセッション内でServiceBを起動します。サービスBはServiceAと同じバージョンのユーティリティを使用します。サービスBが開始されます。

ユーザーは、同じセッション内でServiceCを起動します。 ServiceCは異なるバージョンのユーティリティで構築されました。 ServiceCが読み込まれません。

ユーザーがアプリケーションを再起動し、ServiceCを最初にロードしようとします。 ServiceCは正常に読み込まれます。その後、同じセッション内で、ユーザーはServiceAまたはServiceBの貸し出しを試み、両方が失敗します。

何らかの理由で、サービスが最初のサービスの後に読み込まれると、実際にアセンブリを解決できない場合でも、AssemblyResolveが再び呼び出されません。イベントが発生した場合、ハンドラは現在ロードされているユーティリティのバージョンを返します。

+0

あなたのプロジェクトの構成を正確に見ることなく、何が起こっているのかを確かめることは不可能です。 Utility.dllのリファレンスで「特定のバージョン」が「true」に設定されているサービス(または少なくともServiceA)をコンパイルしたばかりのようです。あなたは、参照されているものと同じか、それより新しいバージョンであれば、存在するバージョンを使用することができます。これらは便利です:https://stackoverflow.com/questions/37431249/whats-the-recommended-way-to-run-plugins-with-dependency-dlls-that-have-differおよびhttps:// stackoverflow。 com/questions/93455/assembly-names-and- –

+0

「特定のバージョン」の設定が最初に見たものです。すべてが "false"に設定されています。 – James

+0

複数のバージョンを読み込むことができます(GACにすべて入れた場合を除いて、別の場所から手作業でインストールする可能性があります)か、親アプリケーションのアセンブリリダイレクトを最高バージョンにするよりも、アセンブリに署名(厳密に名前が付けられています) –

答えて

0

詳細なしで外出いただきまし知ることは難しい(サービスAとサービスBの独立した.dllです?またはそれらは同じ.exeのアセンブリにある?)しかし、私はこの方法でそれを行います:

1)AppDomain.CurrentDomain.AssemblyResolveメカニズムは、障害検出のためにのみ使用します。しかし、まずは.Net AppDomainロジックを使わずにそれを手に入れようとします。私Loader_AssemblyResolve私はいくつかの時間は、Webページから学ぶ、しかし、今、私がURLのために見て(ようこそ貢献のために恥ずかしい怠惰だ最もよく知られているプラ​​クティスを使用して

MyDomain.AssemblyResolve += new ResolveEventHandler(Loader_AssemblyResolve); 

if (System.IO.File.Exists(pathToAssembly) == false) 
{ 
    System.IO.File.Copy(KnownPathToLastVersion, pathToAssembly) 
} 

_assembly = Assembly.Load(AssemblyName.GetAssemblyName(pathToAssembly)); 

2)):!

private Assembly Loader_AssemblyResolve(object sender, ResolveEventArgs args) 
    { 
     Assembly assembly = null; 
     //1. Disconnect the event AssemblyResolve 
     _Domain.AssemblyResolve -= new ResolveEventHandler(Loader_AssemblyResolve); 
     try 
     { 
      //2. Do not try to get the file without looking first 
      // in memory. AssemblyResolve could fire even when the 
      // Assembly is already loaded 
      assembly = System.Reflection.Assembly.Load(args.Name); 
      if (assembly != null) 
      { 
       _Domain.AssemblyResolve += new ResolveEventHandler(Loader_AssemblyResolve); 
       return assembly; 
      } 
     } 
     catch 
     { // ignore load error 
     } 

     //3. Then try to get it from file 
     string FileName=GetFileName(args.Name); 
     try 
     { 
      assembly = System.Reflection.Assembly.LoadFrom(FileName); 
      if (assembly != null) 
      { 
       _Domain.AssemblyResolve += new ResolveEventHandler(Loader_AssemblyResolve); 
       return assembly; 
      } 
     } 
     catch 
     { // ignore load error 
     }    
     //Be sure to reconnect the event 
     _Domain.AssemblyResolve += new ResolveEventHandler(Loader_AssemblyResolve); 

     return assembly; 
    } 

私はあなたに役立つことを願っています。

関連する問題