2013-07-11 3 views
5

그래서 나는 AppDomain.CurrentDomain.AssemblyResolve을 사용하여 DLL을 임베디드 리소스에서 런타임에 (IlMerge를 사용하지 않고) 플러그인으로로드하는 방법을 참조했습니다. 그러나이 코드를 삽입하면 플러그인이 주 라이브러리의 TypeLoadException 메시지를 throw하기 전에 이벤트 처리기가 스레드를받지 못합니다.AppDomain.CurrentDomain.AssemblyResolve in dynamics crm

코드를 정적 생성자, Execute 메서드 내부 및 주 생성자에 배치하려고했습니다. 이벤트 처리기가 등록되었지만 처리기의 중단 점이 적중되지는 않습니다.

환경은 최신 롤업과 SDK 개발자 도구 플러그인 프로젝트 및 플러그인 클래스 생성을 사용하는 Dynamics CRM 2011입니다.

누구나 내가이 작업을 수행하기 위해해야 ​​할 일을 알고 있습니까?

답변

4

콜백을 통해로드하려는 어셈블리에서 모든 유형을 참조하기 전에 AssemblyResolve 이벤트 등록이 발생하는 것이 중요합니다. 정적 생성자에서 유형을 참조하지 않더라도 외부 어셈블리의 참조 유형 인 클래스의 기본 변수 클래스 또는 기본 클래스가 없는지 확인해야합니다. 여기

내가 그것을 할 방법은 다음과 같습니다 내가 어셈블리로드를 처리 할 수있는 별도의 클래스를 가지고 적절하게 이름을 AssemblyLoader :

using System; 
using System.IO; 
using System.Linq; 
using System.Reflection; 

internal static class AssemblyLoader 
{ 
    internal static void RegisterAssemblyLoader() 
    { 
     AppDomain currentDomain = AppDomain.CurrentDomain; 
     currentDomain.AssemblyResolve -= OnResolveAssembly; 
     currentDomain.AssemblyResolve += OnResolveAssembly; 
    } 

    private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args) 
    { 
     return LoadAssemblyFromManifest(args.Name); 
    } 

    private static Assembly LoadAssemblyFromManifest(string targetAssemblyName) 
    { 
     Assembly executingAssembly = Assembly.GetExecutingAssembly(); 
     AssemblyName assemblyName = new AssemblyName(targetAssemblyName); 

     string resourceName = DetermineEmbeddedResourceName(assemblyName, executingAssembly); 

     using (Stream stream = executingAssembly.GetManifestResourceStream(resourceName)) 
     { 
      if (stream == null) 
       return null; 

      byte[] assemblyRawBytes = new byte[stream.Length]; 
      stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length); 


      return Assembly.Load(assemblyRawBytes); 
     } 

    } 

    private static string DetermineEmbeddedResourceName(AssemblyName assemblyName, Assembly executingAssembly) 
    { 
     //This assumes you have the assemblies in a folder named "EmbeddedAssemblies" 
     string resourceName = string.Format("{0}.EmbeddedAssemblies.{1}.dll", 
              executingAssembly.GetName().Name, assemblyName.Name); 

     //This logic finds the assembly manifest name even if it's not an case match for the requested assembly       
     var matchingResource = executingAssembly.GetManifestResourceNames() 
               .FirstOrDefault(res => res.ToLower() == resourceName.ToLower()); 

     if (matchingResource != null) 
     { 
      resourceName = matchingResource; 
     } 
     return resourceName; 
    } 
} 

그런 다음, 내 플러그인 클래스에서, 나는 나의 AssemblyLoader에 전화를 정적 생성자를 사용하여 . 로직을 별도의 클래스로 옮김으로써, 필자가 플러그인 클래스의 정적 컨텍스트에서 참조하는 타입의 수를 제한함으로써 실수로 외부 어셈블리에있는 것을 참조하는 위험을 줄일 수 있습니다. 내 플러그인 클래스는 전혀 사용하지 않도록

using System; 
using Microsoft.Xrm.Sdk; 

public class MyPlugin : IPlugin 
{ 
    static MyPlugin() 
    { 
     AssemblyLoader.RegisterAssemblyLoader(); 
    } 

    public void Execute(IServiceProvider serviceProvider) 
    { 
     //... 
    } 
} 

는 또한 다른 클래스로 거의 모든 외부 어셈블리의 용도의 이동. 대부분 이것은주의가 부족합니다.

+0

ok ... 당신의 솔루션이 내가 게시 한 것과 실질적으로 다른 것은 아닙니다 ... 그러나 문제의 복잡성이 감소되고 점진적으로 증가하면 문제가 지속될 지 궁금해 지므로 체크 표시가 나타납니다. –

+0

사용자 지정 워크 플로와 동일한 기술을 사용할 수 있습니까? –