2017-02-01 1 views
6

私が開発に役立つシステムの要件の1つに、ファイルをインポートする機能があり、さまざまな種類のファイル(csv、xmlなど)を処理するための一連のアダプタがあります。私たちは遭遇すると予想します。開発の初期段階では、リファレンスとコマンドを使用してデータアダプタをハードコーディングしていました。明らかにこれが生きるときには、新しいアダプタを作成してdllをフォルダに入れ、コードを再コンパイルせずにプロシージャを実行できる状況が必要です。多くのdllをコードにロードするとクラッシュする

これを実装するには、this questionのコードを修正しました。この方法は、我々は成功し、データアダプタをロードし、私が期待するようにそれらを使用することができます。この

private IEnumerable<IUniversalDataAdapter> DataAdapters 
{ 
    get 
    { 
     foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) 
     { 
      foreach (var type in asm.GetTypes().Where(x => x.GetInterfaces().Contains(typeof(IUniversalDataAdapter)))) 
      { 
       if (type.IsAbstract) continue; // can't create abstract classes 

       if (!dataAdapters.Any(y => y.GetType().Equals(type))) 
       { 
        IUniversalDataAdapter adapter = (IUniversalDataAdapter)Activator.CreateInstance(type); 
        dataAdapters.Add(adapter); 
       } 
      } 
     } 
     return dataAdapters; 
    } 
} 

を持って実行したときに

string dllLocation = @"C:MyLocation\dllLocation"; 
DirectoryInfo dir = new DirectoryInfo(dllLocation); 
var tempfiles = dir.GetFiles("*Adapter*.dll", SearchOption.AllDirectories);  // This will need to be changed when we go live 

    foreach (var file in tempfiles) 
    { 
     Assembly tempAssembly = null; 

     //Before loading the assembly, check all current loaded assemblies in case already loaded 
     //has already been loaded as a reference to another assembly 
     //Loading the assembly twice can cause major issues 
     foreach (Assembly loadedAssembly in AppDomain.CurrentDomain.GetAssemblies()) 
     { 
      //Check the assembly is not dynamically generated as we are not interested in these 
      if (loadedAssembly.ManifestModule.GetType().Namespace != "System.Reflection.Emit") 
      { 
       //Get the loaded assembly filename 
       string loadedFilename = loadedAssembly.CodeBase.Substring(loadedAssembly.CodeBase.LastIndexOf('/') + 1); 

       //If the filenames match, set the assembly to the one that is already loaded 
       if (loadedFilename.ToUpper() == file.Name.ToUpper()) 
       { 
        tempAssembly = loadedAssembly; 
        break; 
       } 
      } 
     } 

     //If the assembly is not aleady loaded, load it manually 
     if (tempAssembly == null) 
     { 
      tempAssembly = Assembly.LoadFrom(file.FullName); 
     } 

     Assembly a = tempAssembly; 

その後、次のように問題のコードは、コンストラクタに位置しています。私は

var tempfiles = dir.GetFiles("*.dll", SearchOption.AllDirectories); 

ルーチンクラッシュにそれを変更すると、コードの第2ビットは、この時に実行される前に、私たちはライン

var tempfiles = dir.GetFiles("*Adapter*.dll", SearchOption.AllDirectories); 

を持っているコードの最初のビットに質問に対する今

、ルーチン

private IEnumerable<IDataValidator> DataValidators 
{ 
    get 
    { 
     if (validators.Count == 0) 
     { 
      foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) 
      { 
       foreach (var type in asm.GetTypes().Where(x => x.GetInterfaces().Contains(typeof(IDataValidator)))) 
       { 
        if (!type.IsAbstract) 
        { 
         var validator = (IDataValidator)Activator.CreateInstance(type, context); 
         validators.Add(validator); 
        } 
       } 
      } 
     } 
     return validators; 
    } 
} 

編集:例外を追加しました

System.Reflection.ReflectionTypeLoadException was unhandled 
    HResult=-2146232830 
    Message=Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. 
    Source=mscorlib 
    StackTrace: 
    at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module) 
    at System.Reflection.RuntimeModule.GetTypes() 
    at System.Reflection.Assembly.GetTypes() 
    at TTi.Data.Pipeline.Server.Common.DataPipeline.get_DataValidators() in C:\Users\anorcross\Source\Workspaces\Universal System\Data\Main\TTi.Data\TTi.Data.Pipeline.Server.Common\DataPipeline.cs:line 124 
    at TTi.Data.Pipeline.Server.Common.DataPipeline.CheckConfiguration(DataConfiguration config) in C:\Users\anorcross\Source\Workspaces\Universal System\Data\Main\TTi.Data\TTi.Data.Pipeline.Server.Common\DataPipeline.cs:line 528 
    at TTi.Data.Pipeline.Server.Common.DataPipeline.ProcessDataSource(IDataSource dataSource, DataConfiguration config) in C:\Users\anorcross\Source\Workspaces\Universal System\Data\Main\TTi.Data\TTi.Data.Pipeline.Server.Common\DataPipeline.cs:line 213 
    at TTi.Data.Test.Program.ImportTest(String testFolders) in C:\Users\anorcross\Source\Workspaces\Universal System\Data\Main\TTi.Data\TTi.Data.Test\Program.cs:line 362 
    at TTi.Data.Test.Program.Main(String[] args) in C:\Users\anorcross\Source\Workspaces\Universal System\Data\Main\TTi.Data\TTi.Data.Test\Program.cs:line 48 
    at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) 
    at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) 
    at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 
    at System.Threading.ThreadHelper.ThreadStart_Context(Object state) 
    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
    at System.Threading.ThreadHelper.ThreadStart() 
InnerException: 

EndEdit:

DataValidatorsは間違いDataAdaptersルーチンの前に実行されます。 DataValidatorsは、メインコードベースに含まれる単なるコードの一部であり、インポートされたデータが期待される形式であることを確認するためにチェックします。この時点で、必要なものが存在することを確認できるように、ロードするだけです。

ロードされたアセンブリを見ると、両方のバージョンのコードでは必要に応じてアダプタが読み込まれますが、2番目のバージョンは最初のバージョンよりも先に読み込まれます。

だから、tempfilesの2番目のバージョンは、コードのまったく無関係な部分のように見えるのはなぜですか?十分なデータアダプタを追加すると、コードがクラッシュする可能性がありますか?

+3

例外とスタックトレースとは何ですか? – rene

+1

いくつかの質問:.dllで終わるすべてのファイルは本当に.net dllですか?管理されていないDLLを読み込もうとすると、クラッシュします。あなたのdllによって同じ場所に参照されるdllはありますか?またはシステムがそれらを見つける可能性がある場所(つまり、binフォルダまたは%windir%\ system32など)に移動しますか?あなたは64ビットのプロセスとして実行していますが、読み込みしようとしているアセンブリのいずれかが32ビットのアンマネージdllを参照していますか(これもクラッシュします)? – mortb

+0

@mortbはい、彼らが私たちのコードやNugetパッケージのすべてのビットであることがわかります。このプログラムは、32ビットまたは64ビットについては特定されておらず、.Net 4.6を実行しています。私は特にソリューションフォルダを探しています。 – Andrew

答えて

1

この例外は、必要なDLLが見つからないためロードできないことを示します。今、彼が探していたDLLとコンパイル時に失敗しなかった理由は何ですか?最初の質問に答えるためには、深く調査する必要があります。そして、最終的に、あなたのコードは、あなたが使用していない、または必要でないいくつかの奇妙なdllを読み込んでいます。これは、ロードされたすべてのアセンブリのすべての型に対して反復処理を行うコードが原因で発生します。あなたが参照していない他のdllからいくつかの型を使用する型を公開するdllを参照として持つことは可能です。あなたがそのタイプを使用しない限り、それは問題ありません。

これを修正するには、ループをより具体的にする必要があります。あなたのアダプターはどんなシステムDLLでも宣言されないので、なぜそれらを反復するのでしょうか?それは、より特異的であるため

foreach (var asm in AppDomain.CurrentDomain.GetAssemblies().Where(IsMyAssembly)) 
      { 
       foreach (var type in asm.GetTypes().Where(x => x.GetInterfaces().Contains(typeof(IDataValidator)))) 
       { 
//... 

Adapterとあなたの最初のファイルのスキャンは、それなしで1よりも優れています。私はあなたのコードをさらに具体的にすることをアドバイスします。これは、例えば、いくつかの設定ファイルの型の明示的な登録によって、これはdllスキャンを完全に排除します。

+0

私はアダプタでバージョンを使いたいと思っていましたが、私が聞いたところ、アダプタの名前に依存することはできませんでした。彼らがすべてアダプターになるので私には奇妙に見えますが、なぜそれを呼び出さないのですか?私は設定ファイルを使用するという考えを呼び起こすでしょう、とにかくそれらの番号を持っています – Andrew

+0

これは問題のようです。私はいくつかのカウントをしました(私はおそらくこれまでにすべきでした)。何もロードせずに26個のアセンブリがロードされました。 *アダプター*では、これまで32個のアダプターがコード化されていました。ちょうど* 94がロードされていたので、余分な62のアセンブリのどこかにおそらく何か不具合がありました。私はシステムに非コーディングの変更を行います:) – Andrew

1

dllからすべての一致する型を取得する関数を書いたときと全く同じ問題がありました。 try catchに注意してください。

private static IEnumerable<Type> MatchingTypesFromDll<TBaseType>(string dllPath) 
    { 
     try 
     { 

      return Assembly 
       .LoadFrom(dllPath) 
       .GetExportedTypes() 
       .Where(TypeSatisfies<TBaseType>); 

     } 
     catch (Exception e) 
     { 
       Debug.WriteLine($"Exception {e} when trying to load from {dllPath}"); 
       return new Type[] {}; 
     } 
    } 

例外を無視しました。ナゲットパッケージでさえ、いくつかのネイティブdllやおそらく.dllという名前のファイルがあります。例外をキャッチし、破損したファイルを無視することは、管理されているDLLとそうでないDLLを先取りして調べるほうが簡単ではありません。

+0

私は理由が理解できることに興味があります。 – peval27

+0

Oooops ..私はいくつかの無関係のものを削除するコードをきれいにしようとしました。私はそれを修正します。 – bradgonesurfing

+0

代わりに、 'dll'のすべてを適切にフィルタリングし、正しいものだけを読み込もうとすると、OPコードがスローする(' ReflectionTypeLoadException')のようなものではなく、一般的な 'Exception'をキャッチして無視しますそれ?あなたは例外をキャッチすることができましたが? – TheLethalCoder

関連する問題