私が開発を支援しているシステムの要件の1つは、ファイルをインポートする機能であり、遭遇すると予想されるさまざまなタイプのファイル(csv、xmlなど)を処理するためのアダプターのセットがあります。開発の初期段階では、参照を介してコマンドを使用してデータアダプタをハードコーディングしていました。明らかに、これが稼働するときは、新しいアダプターを作成し、dllをフォルダーにスローして、コードを再コンパイルせずにプロシージャを実行できる状況が必要になります。
これを実装するために、私はこの質問のコードを適応させました。問題のコードは、次のようにコンストラクターにあります。
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;
後でメソッドを実行すると、これがあります
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;
}
}
これにより、データアダプターが正常に読み込まれ、期待どおりに使用できるようになります。
さて、質問ですが、コードの最初のビットに次の行があります
var tempfiles = dir.GetFiles("*Adapter*.dll", SearchOption.AllDirectories);
に変更すると
var tempfiles = dir.GetFiles("*.dll", SearchOption.AllDirectories);
コードの2番目のビットがこのルーチンで実行される前に、ルーチンがクラッシュします
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番目のバージョンのクラッシュが発生するのでしょうか。また、十分なデータアダプターを追加すると、コードがクラッシュしますか?
この例外は、必要な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
は、より具体的であるため、それを使用しない場合よりも優れています。たとえば、一部の構成ファイルに型を明示的に登録することで、コードをさらに具体的にすることをお勧めします。これにより、dllスキャンが完全に排除されます。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加