2013-12-17 2 views
2

몇 가지 메서드를 정의하는 인터페이스를 사용하는 여러 끝점 (C# 클래스)이있는 예제 응용 프로그램이 있습니다. 이 엔드 포인트는 자체 클래스 라이브러리에 있습니다. "EndPoints.EndPoint1"라는 어셈블리에AppDomain 만들기 및 하위 폴더에있는 어셈블리에서 메서드 호출

라는 어셈블리에서

에게 "엔드 포인트"

namespace EndPoints 
{ 
    public interface IEndPoint 
    { 
     void Initialize(XmlDocument message); 
     bool Validate(); 
     void Execute(); 
    } 
} 

namespace EndPoints 
{ 
    public class EndPoint1 : IEndPoint 
    { 
     private XmlDocument _message; 

     public void Initialize(XmlDocument message) 
     { 
      _message = message; 
      Console.WriteLine("Initialize EndPoint1"); 
     } 

     public bool Validate() 
     { 
      Console.WriteLine("Validate EndPoint1"); 
      return true; 
     } 

     public void Execute() 
     { 
      Console.WriteLine("Execute EndPoint1"); 
     } 
    } 
} 

사용하고 해당 클래스를 찾을 엔드 포인트를 "선택"할 응용 프로그램 , 그것의 인스턴스를 생성 한 다음 차례대로 메소드를 호출하십시오.

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      // Generate "random" endpoint name 
      string endPointName = GetEndPointName(); 

      // Get the class name from the namespaced class 
      string className = GetClassName(endPointName); 

      // Dummy xmldocument that used to pass into the end point 
      XmlDocument dummyXmlDocument = new XmlDocument(); 

      // Load appropriate endpoint assembly because the application has no reference to it so the assembly would not have been loaded yet 
      LoadEndPointAssembly(endPointName); 

      // search currently loaded assemblies for that class 
      var classTypes = AppDomain.CurrentDomain.GetAssemblies() 
       .SelectMany(s => s.GetTypes()) 
       .Where(p => p.FullName == endPointName) 
       .ToList(); 

      // cycle through any found types (should be 1 only) 
      for (int i = 0; i < classTypes.Count; i++) 
      { 
       var classType = classTypes[i]; 
       IEndPoint classInstance = Activator.CreateInstance(classType) as IEndPoint; 

       classInstance.Initialize(dummyXmlDocument); 

       if (classInstance.Validate()) 
       { 
        classInstance.Execute(); 
       } 
      } 
     } 

     private static void LoadEndPointAssembly(string endPointName) 
     { 
      using (StreamReader reader = new StreamReader(endPointName + ".dll", System.Text.Encoding.GetEncoding(1252), false)) 
      { 
       byte[] b = new byte[reader.BaseStream.Length]; 

       reader.BaseStream.Read(b, 0, System.Convert.ToInt32(reader.BaseStream.Length)); 
       reader.Close(); 

       AppDomain.CurrentDomain.Load(b); 
      } 
     } 

     private static string GetEndPointName() 
     { 
      // Code to create "random" endpoint class name 
      Random rand = new Random(); 
      int randomEndPoint = rand.Next(1, 4); 
      return string.Format("EndPoints.EndPoint{0}", randomEndPoint); 
     } 

     private static string GetClassName(string namespacedClassName) 
     { 
      string className = null; 
      string[] components = namespacedClassName.Split('.'); 

      if (components.Length > 0) 
      { 
       className = components[components.Length - 1]; 
      } 

      return className; 
     } 
    } 
} 

다음을 달성하기 위해 응용 프로그램을 변경하고 싶습니다.
- 각 끝점 어셈블리 (및 사용되는 모든 구성 파일 및/또는 다른 어셈블리)는 응용 프로그램 폴더 아래의 하위 폴더에 들어 있습니다. 하위 폴더 이름은 끝점 클래스의 이름이됩니다. "EndPoint1" - 각 엔드 포인트는 자체 appdomain에서 실행됩니다.

그러나 지금까지이 문제를 해결할 수 없었습니다. appdomain을 만들 때 AppDomainSetup의 ApplicationBase 및 PrivateBinPath 속성을 설정하여 사용할 하위 폴더를 지정하더라도 적절한 어셈블리를로드하지 못했다는 예외가 계속 발생합니다. 예 :

AppDomain appDomain = null; 
AppDomain root = AppDomain.CurrentDomain; 
AppDomainSetup setup = new AppDomainSetup(); 
setup.ApplicationBase = root.SetupInformation.ApplicationBase + className + @"\"; 
setup.PrivateBinPath = root.SetupInformation.ApplicationBase + className + @"\"; 

appDomain = AppDomain.CreateDomain(className, null, setup); 

그런 다음 새로 생성 된 appDomain에서 Load 메서드를 사용하여 어셈블리를로드하려고했습니다. 그 때 오류가 발생합니다.

해당 어셈블리를로드하고 인터페이스에 정의 된 메서드를 호출하는 방법에 대한 의견이 있으십니까? 많은 감사합니다.

+0

AppDomain.Load 메서드는 찾고있는 것과 다른 현재 AppDomain에서 어셈블리를로드하고 있습니다. 미샬의 대답과 같은 MarshalByRefDerived 로더가 필요합니다. –

답변

2

다음과 같은 방식으로 처리합니다. 먼저 MarshalByRef에서 파생 된 클래스가 필요합니다. EndPoints을로드하고 별도의 응용 프로그램 도메인에서 실행해야합니다. 여기에, 나는 그것이의 ConsoleApplication1에 정의되어 있다고 가정하지만, 다른 곳으로 이동할 수 있습니다 : 여기

public class EndPointLoader : MarshalByRefObject { public void Load(string path, string endPointName) { Assembly.LoadFrom(path); var classTypes = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(s => s.GetTypes()) .Where(p => p.FullName == endPointName) .ToList(); for (int i = 0; i < classTypes.Count; i++) { .... } } } 

이 클래스를 사용하는 코드입니다. LoadEndPointAssembly 메소드에 넣을 수 있습니다.

var appDomain = AppDomain.CreateDomain(endPointName); 
var loader = (EndPointLoader)appDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(EndPointLoader).FullName); 
loader.Load(assemblyPath, endPointName);