Silverlight/Workshop

실버라이트 어셈블리(Dll) 동적 로딩 팁.

길버트리 2009. 1. 4. 02:21

얼마 전 (2008/12/27)에 실버라이트 카페에 클라인스님이 포스팅한
동적 로드한 Assembly에서 컨트롤 생성하기’란 글을 보셨는지요.

google에서 검색하면 Assembly에 대한 동적 로딩에 대한 소개를 하고 있는 글은
대부분이 이 방법으로 소개가 되어 있습니다.

언젠가 boxmile이 알려줘서 봤던 JEFF PROSISE의 포스팅 중 흥미로운 내용이 있어서 소개합니다.

이 내용은 동적로드한 Dll을 사용하면서도, 참조를 통해 Type 캐스팅하여 구현을 하고 싶을 때 유용합니다.
다시 말해 필요에 의해(On demand) 어셈블리를 다운로드 받게 구성해 놓은 상태로, 어떤 어셈블리 Dll이 다운로드 될 것이고 그 어셈블리로부터 어떤 클래스를 생성해 사용할 것인지 명확하게 아는 상황입니다.

아래의 상황1과 상황2를 비교해 보세요.

상황1.

AssemblyPart part = new AssemblyPart();
Assembly a = part.Load(e.Result);
Object obj = (Object)a.CreateInstance("Foo");

상황2.

AssemblyPart part = new AssemblyPart();
Assembly a = part.Load(e.Result);
Foo foo = new Foo();

생성된 instance로 뭔가 더 구현을 더 하고 싶다면, 개발자는 당연히 상황2를 선호하겠죠.
상황2를 연출할 수 있는 방법을  소개합니다. (엄밀히 이야기하면 100% 상황2의 상황은 아니지만 비슷합니다.)

그러기 위해서는 일단 아래 3단계의 준비가 필요합니다.

Step 1. 동적로드할 어셈블리를 실버라이트 프로젝트에 ‘참조추가’메뉴로 추가를 하고, 참조 속성 중 ‘Copy Local’ 속성을 false로 설정합니다. 이것은 Visual Studio가 빌드를 할 때, 해당 어셈블리를 xap 파일에 함께 패키징하는 것을 방지합니다.

Step 2. 실행하면, WebClient를 이용하여 어셈블리를 다운받고 AssemblyPart.Load 메서드를 이용하여 어셈블리를 로드합니다.

Step 3. 가장 중요한 부분인데요. 분리된 메서드를 호출하여 어셈블리의 어떤 Type을 생성하도록 구현을 하고, 그 메서드는 MethodImpl(MethodImplOptions.NoInlining) 속성을 문두에 선언해 줍니다.
그리고 동적로드한 어셈블리 안의 어떤 Type도 어셈블리를 로드하는 메서드 안에서는 사용하지 않습니다. 

제가 일단 샘플 프로젝트를 만들어 보았습니다.


샘플 프로젝트 다운로드



void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if (e.Cancelled == true || e.Error != null)
 return;

    AssemblyPart part = new AssemblyPart();
    Assembly assembly = part.Load(e.Result);

    //MyWidget widget = new MyWidget();
    //layoutWidgetHolder.Children.Clear();
    //layoutWidgetHolder.Children.Add(widget);
    AddWidget();
}

[System.Runtime.CompilerServices.MethodImpl
    (System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]

private void AddWidget()
{
    MyWidget widget = new MyWidget();
    layoutWidgetHolder.Children.Clear();
    layoutWidgetHolder.Children.Add(widget);
}



DynamicAssemblyLoading프로젝트의 Page.xaml.cs의 wc_OpenReadCompleted 메서드 부분을 보시면,
주석 처리된 부분이 있습니다. 위에 설명한 상황2처럼 구현을 해 본 부분인데요. 이렇게 해서 실행을 하면,
아래와 같은 에러가 발생합니다. Step 3에서 설명한 부분을 기억하세요.

 

Step 3을 명심하고, AddWidget 같은 메서드로 분리하여, 그 안에서 Type을 사용하면,
아래 스냅샷처럼 정상적인 결과를 얻을 수 있습니다.

 



Step 3의 MethodImpl(MethodImplOptions.NoInlining) 선언은 안 하고도 테스트 해보았는데,
없어도 잘 됩니다. 하지만, Jeff Prosise에 의하면 잘되어도 운이 좋아서 잘 될 뿐이니,
선언을 명시적으로 해주는 것이 안전하다고 하네요.