2016-11-29 18 views
0

C : \ MyApp에있는 콘솔 앱이 있습니다.C#, Activator.CreateInstance (assemblyType) 객체가 참조하는 DLL 가져 오기

저는 앱에서 참조하지 않는 여러 라이브러리가 있습니다. Activator.CreateInstance()를 사용하여 사용합니다. 그들은 C : \ MyLibrary \ Job001, C : \ MyLibrary \ Job002 등에 있습니다. 이 라이브러리들 각각은 다중 의존성을 가지며, 주 앱에 이미 존재하는 서로 다른 버전의 의존성 일 수 있습니다.

실행하려고하면이 오류가 표시됩니다. Could not load file or assembly 'Persistence.Database, Version=1.7.2.67, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified. 이것은 대부분의 작업에 공통적 인 종속성 중 하나입니다. 디렉토리를 확인했는데 라이브러리가 존재합니다.

라이브러리를 활성화하려면 어떻게합니까? 해당 디렉토리에있는 참조를 사용합니까? 나는 도서관을 활성화하기 위해 다음 (확장) 코드를 사용하고

: 나는 system.appdomain.assemblyresolve에서 찾고 있지만 라이브러리 프로젝트에서 이것을 사용하는 방법을 의 감각을하고 있지 않다

public static IJob ConcreteJob(this JobInfoPayload src) 
{ 
    if (src.AssemblyFile.IsNullOrEmpty()) 
     throw new Exception("AssemblyFile cannot be empty or null!"); 
    if (src.AssemblyName.IsNullOrEmpty()) 
     throw new Exception("AssemblyName cannot be empty or null!"); 

    try 
    { 
     var assembly = Assembly.LoadFile(src.AssemblyFile); 
     var assemblyType = assembly.GetType(src.AssemblyName); 
     var job = Activator.CreateInstance(assemblyType) as IJob; 
     return job; 
    } 
    catch (Exception ex) 
    { 
     Serilog.Log.Logger.Fatal(ex, "JOB was not able to be created!!"); 
     throw; // bubble this up to the top... 
    } 
} 

.

생각하십니까?


추가 정보 (2016 11월 29일)

서버 응용 프로그램 참조 :

  • Library.Infrastructure
  • QueueApp.Core
  • Hangfire
  • OWIN
,

작업 라이브러리 참조 :

  • Library.Infrastructure
  • Library.Persistence
  • Library.SQL.Database01
  • Library.SQL.Database02
  • QueueApp.Job .Core
  • (210)
  • EntityFramework는

우리는 몇 가지 채용 하지만작업 도서관의 다른 버전을 참고로 구축 할 수있는 동일한 패턴을 따르는 있습니다. 이것은 시간이 지남에 따라 느린 크리프가 원인입니다. 작년에 작성된 작업이 여전히 작동하는 경우 왜 솔루션을 열어 모든 참조를 업데이트하고 다시 컴파일 한 다음 QA 및 승인을 거쳐 한 달을 보내면 그냥 두어도됩니까?

내가 겪고있는 도전 과제는 참조 된 파일을 찾을 수 없어서 서버 응용 프로그램 디렉토리에 있어야한다는 것입니다. 대신 그들은 그 욥의 디렉토리에 있습니다. Fuslogvw.exe를 사용하면 DLL의 디렉토리가 아니라 호스팅하는 응용 프로그램의 디렉토리가 표시됩니다.

** Assembly.LoadFrom() 또는 Assembly.LoadFile()을 사용하는지 여부에 관계없이 현재 동일한 문제가 발생합니다.

FUSLOGVW 로그 결과 :
D : 응용 프로그램이 모든 파일을 찾고

*** Assembly Binder Log Entry (11/29/2016 @ 10:20:21 AM) *** 

The operation failed. 
Bind result: hr = 0x80070002. The system cannot find the file specified. 

Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll 
Running under executable D:\Dev\QueueApp\Source\QueueApp\bin\Debug\QueueApp.exe 
--- A detailed error log follows. 

=== Pre-bind state information === 
LOG: DisplayName = Job.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null (Fully-specified) 
LOG: Appbase = file:///D:/Dev/QueueApp/Source/QueueApp/bin/Debug/ 
LOG: Initial PrivatePath = NULL 
LOG: Dynamic Base = NULL 
LOG: Cache Base = NULL 
LOG: AppName = QueueApp.exe 
Calling assembly : Job.AgileExport, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null. 
=== 
LOG: This bind starts in default load context. 
LOG: Using application configuration file: D:\Dev\QueueApp\Source\QueueApp\bin\Debug\QueueApp.exe.Config 
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config. 
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind). 
LOG: Attempting download of new URL file:///D:/Dev/QueueApp/Source/QueueApp/bin/Debug/Job.Core.DLL. 
LOG: Attempting download of new URL file:///D:/Dev/QueueApp/Source/QueueApp/bin/Debug/Job.Core/Job.Core.DLL. 
LOG: Attempting download of new URL file:///D:/Dev/QueueApp/Source/QueueApp/bin/Debug/Job.Core.EXE. 
LOG: Attempting download of new URL file:///D:/Dev/QueueApp/Source/QueueApp/bin/Debug/Job.Core/Job.Core.EXE. 
LOG: All probing URLs attempted and failed. 

을 \ 데브 \ QueueApp \ 소스 \ QueueApp \ 빈 \ 디버그
작업이 존재 :
를 D : \ Dev \ QueueApp \ Source \ Job.AgileExport \ bin \ Debug

+0

결코 절대로 LoadFile()을 사용하지 마십시오. 항상 LoadFrom()을 사용하지 마십시오. 그래도 문제가 지속되면 Fuslogvw.exe –

+0

을 LoadFrom()으로 전환해도 여전히 같은 오류가 발생합니다. (위의 추가 정보 참조) –

+0

다른 어셈블리를로드하고 사용할 수 있습니까? – McNets

답변

0

이것은 내가 지금까지 생각해 낸 것입니다. 이 클래스는 주 서버 앱에 있으며 JOB에서는 찾을 수 없습니다. 우리에게는 몇 가지 유형의 직업이 있으며 Ad Hoc이 유형 중 하나입니다. 코드를 기본 클래스에 배치하면 모든 JOB 핸들러가이를 상속합니다.

public class JobAdHocHandler : BaseHandler, IJobHandler 
{ 
    public MinimumResultModel Handle(MinimumCommandModel message) 
    { 
     var result = new MinimumResultModel {Id = "-1", PayloadAsString = message.FullPayloadString}; 

     try 
     { 
      var info = message.MinimumPayload.JobInfo; 

      SetupInstance(info); // <<-- SOLUTION (in BaseHandler) 
      var job = JobHandler.GetJob(info); // <<-- SOLUTION (in BaseHandler) 

      result.Id = BackgroundJob.Enqueue(() => job.Execute(null, message.FullPayloadString, JobCancellationToken.Null)); 
     } 
     catch (Exception ex) 
     { 
      Log.Logger.Fatal(ex, ex.Message); 
      result.Exception = ex; 
     } 

     AppDomain.Unload(JobAppDomain); 
     return result; 
    } 
    public bool AppliesTo(JobType jobType) => jobType == JobType.AdHoc; 
} 

public class BaseHandler : MarshalByRefObject 
{ 
    protected internal AppDomain JobAppDomain; 
    protected internal BaseHandler JobHandler; 

    protected internal void SetupInstance(JobInfoPayload info) 
    { 
     var ads = new AppDomainSetup 
     { 
      ApplicationBase = new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName, 
      DisallowBindingRedirects = false, 
      DisallowCodeDownload = true, 
      PrivateBinPath = info.JobClassName, 
      ApplicationName = info.JobName, 
     }; 
     JobAppDomain = AppDomain.CreateDomain(info.JobName, null, ads); 
     JobHandler = (BaseHandler)JobAppDomain.CreateInstanceAndUnwrap(typeof(BaseHandler).Assembly.FullName, typeof(BaseHandler).FullName); 
    } 

    protected internal IJob GetJob(JobInfoPayload info) 
    { 
     var assembly = Assembly.LoadFrom(info.JobClassName + @"\" + info.JobClassName + ".dll"); 
     var assemblyType = assembly.GetType(info.AssemblyName); 
     var job = Activator.CreateInstance(assemblyType) as IJob; 
     if (job == null) 
      throw new Exception("Unable to create job: " + info.JobClassName); 
     return job; 
    } 
} 

지금까지는 잘 작동합니다.

1

두 가지 해결 방법이 있다고 생각합니다.

한 가지 해결책은 동적으로로드 된 어셈블리를 호스팅 할 새 AppDomain을 만드는 것입니다. 새 AppDomain을 만들 때 AppDomain에 대한 설정 개체를 제공하는 옵션이 있으며 해당 개체에서 AppDomain이 어셈블리를 확인하는 데 사용할 경로를 제공합니다. 기존 경로 인 AppDomain이 이미 있으므로 경로를 변경할 수 없습니다.

다른 해결책은 정상적인 어셈블리 확인이 실패 할 경우 발생하는 현재 AppDomain의 AssemblyResolve 이벤트를 처리하는 것입니다. 그런 다음 사용자 지정 단계를 수행하여 어셈블리를 해결할 수 있습니다.

.NET이와 경우 BinaryFormatter (예 : IE, COM + 등과 같은) 다양한 용기에 호스팅되는 경우이 이벤트가 사용할 수 있어야 유형을 역 직렬화하는 데 사용됩니다 필요 핸들링 .NET의 기능/버그가 있습니다 실제로 발견되지는 않습니다. 당신은 아마 원래 동적로드 어디에 내 ResolveEventHandler 방법은 폴더의 "실종"어셈블리를 찾기 위해 변경할 수있는 경우 https://github.com/MarimerLLC/csla/blob/V1-5-x/cslacs10/NetRun/Launcher.cs

:

가 나는 후킹의 예를 여기에 AssemblyResolve 이벤트 해결을 어셈블리.

1

Assembly.LoadFrom을 사용하면 동일한 어셈블리의 여러 버전을 동일한 AppDomain에로드 할 수 없습니다.

따라서, Job001 요구 LibraryA, 1.0.0.0 (런타임에 새 버전을 사용할 수 없습니다) 및 Job002 요구 LibraryA, 2.0.0.0, 당신은 자신의 응용 프로그램 도메인에 Job001Job002 각을로드해야합니다. 동적 어셈블리를로드하는 순서가 매우 중요하다

주의 사항 : 그 이후 Job002를로드하는 경우

  • 당신이, 그것을 발견하면 자동으로 LibraryA, 1.0.0.0를로드 Job001을로드 LibraryA, 2.0.0.0LibraryA, 1.0.0.0을로드 할 수 없으므로 도메인에 남아있게됩니다.

  • 마찬가지로 당신이 Job002를로드 할 때 그것을 발견하면, 자동으로 LibraryA, 2.0.0.0을로드하고, 그 후 Job001를로드하는 경우, LibraryA, 1.0.0.0를로드 할 수 없습니다 및 LibraryA, 2.0.0.0 도메인에 남아있게됩니다.

당신은 좋은 방법 중 하나 종속성을 직접로드 Assembly.LoadFile + AppDomain.AssemblyResolve를 사용하는 (다음 같은 응용 프로그램 도메인에서 동일한 어셈블리의 여러 버전을 가질 수 있습니다)입니다 가장, 또는 각 JobXXX 어셈블리에 대한 별도의 응용 프로그램 도메인을 만들 종속성을 자동으로로드하게하십시오.

+0

의미가 있습니다. 너무 많은 의미가 있습니다. AppDomains에 대한 지식이 제한적이라는 사실을 두려워합니다. 나의 첫 번째 반응은 작업마다 새로운 AppDomain을 만드는 것입니다. 자, 충돌하는 조언의 과다로, 이것을 이해하는 가장 쉽고/쉬운 방법입니다. :) –