요즘 너무 블로깅을 안했더니 거미줄이...
본격적인 글은 정신적인 여유가 안되어서 못쓰겠고 대충 메모라도 휘갈겨놓으려고해요.
'가상의' 독자를 상대로 얘기하고는 있지만 기본적으로는 잊지 않으려고 쓰는 '메모'의 용도에요.
친절하지 않은 글이니 관심 없는 분은 그냥 패~스.

Prism + MefExtension을 사용하면 내부적으로 IModuleManager로 MefModuleManager를 사용하는데, 요놈은 XAP 파일에 대한 IModuleLoader로써 다시 MefModuleTypeLoader를 사용하죠. 또한 이 모듈타입로더는 내부적으로 MEF에서 기본으로 제공하는 DeploymentCatalog를 이용해요. ...아 이 무슨 구약적 이름 소개인가; 누가 누구를 낳고 누구는 누구랑 친척이고...;;

여튼;

프리즘의 모듈매니저는 기본적으로 각 모듈과의 의존성 관계를 설정하여 의존성 있는 모듈이 먼저 초기화(Initialize)되도록 처리를 해주는 훌륭한 매커니즘을 가지고 있죠.
문제는 MEF가 기본으로 제공하는 DeploymentCatalog.
요놈은 XAP파일의 URI를 던져주면 알아서 다운로드를 받아 XAP 파일 내에 들어있는 어셈블리들을 죄다 런타임에 불러들인 후 지정된 카탈로그에 ExportPart를 뽑아서 관리해주는 역할까지 수행하죠.
그런데 여기서 문제가 이놈은 다운로드가 완료되면 무조건 위의 동작을 수행한다는거에요.

만약 다음과 같은 의존성을 갖는 솔루션 구성일 경우...
  • 기반 모듈
    • 데이터처리 모듈
      • 이메일 모듈
      • SMS 모듈
    • 그래픽 모듈
'이메일 모듈'은 '데이터처리 모듈'보다 나중에 초기화가 되어야 하죠.
네 실제로 모듈매니저는 의존성 관계를 통해 '데이터처리 모듈'을 먼저 초기화해요.
그런데, DeploymentCatalog에 의해서 각 모듈들이 다운로드 될 때 '이메일 모듈'이 먼저 다운로드가 완료된다면?
내부적으로 '이메일 모듈'을 까뒤집어서 그 안에 있는 ExportPart를 뽑아내려고 시도할거에요.
이 과정에서 '이메일 모듈'이 가지고 있는 '데이터처리 모듈'에 대한 의존성이 문제가 되어 그 시도는 처절한 예외 메시지를 내뱉고는 뻗어버리죠.

혹시 DeploymentCatalog의 소스 코드를 열어보신 분이라면(MEF preview때이건 리플렉터로 까봤건) 아래의 부분이 문제의 그곳. DiscoverParts 메서드의 구현중 일부에요. 밑줄친 부분이 아직 의존성이 해결되었을지 어떨지 알 수 없는 파트를 접근하려는 곳.

foreach (Assembly assembly in assemblies)
{
    if (!dictionary.ContainsKey(assembly.FullName))
    {
        AssemblyCatalog catalog = new AssemblyCatalog(assembly);
        addedDefinitions.AddRange(catalog.Parts);
        dictionary.Add(assembly.FullName, catalog);
    }
}

휘유... 여기까지가 아래에 올린 Don't Panic의 내용을 재현한 거에요.
이게 적당히 다운로드 순서를 바꿔주는 것 만으로는 근본적인 해결이 안되네요.
아무래도 MEF의 DeploymentCatalog를 조금 바꿔서 써야겠어요.
그러려면;; 다시 이걸 사용하는 MefModuleManager를 바꿔야 하고 여기에 연관되어 있는 MefModuleInitializer, MefXapModuleTypeLoader도 모두 조금씩은 바꿔야겠죠; 하아...

그나마 소스 공개되어 있는게 얼마나 다행인지. 오픈소스 만세! 리플렉터 만만세!
저작자 표시 동일 조건 변경 허락
신고
Posted by gongdo
참 뭐랄까 근본 없는 것이란 이런 기초적인데에서 문제를 드러내지요.
그러니까... 보통 예외 메시지들이란게 대체로 별로 도움이 안되는 경우가 많잖아요?
누가 null reference 들어와서 죽은거 모른대요? 왜 null이 들어왔는지를 모르는거죠.

여튼.

이런 경험이 누적되다보면 예외 메시지는 Exception.Message만 대충 보고 다른데서 원인을 찾기 마련인데요.
가끔은 Exception.InnerException을 보고도 더 많은 정보를 얻을 수 있는 기회를 놓칠 때가 있어서 다시는 잊지 말자는 심정으로 포스팅해요.

이번 케이스는 이것.

동적으로 XAP 파일을 받아서 동적으로 어셈블리들을 로드하는데 분명히 해당 어셈블리 중 하나의 의존성이 해결되지 않아서 발생하는 문제인것까진 알겠는데 어떤 어셈블리의 문젠지를 모르겠단 말이에요. 아주 한참을 헤맸죠.

던져주는 메시지라곤 고작 "Unable to load one or more of the requested types. ..."
네, 참 도움 되네요. 로드하다 실패해놓고 로드에 실패했다니;;; 어쩌라고!

뭘 놓쳤는지 아시겠죠? 뒤에 ...으로 더 이상 읽지 않은 부분이 바로: "Retrieve the LoaderExceptions property for more information." 근데 여기에서  InnerException이 null 이라서 '더 많은 정보'가 어디에 있는지를 모르고 지나치기 일쑤란거죠.
사실은 여기에서 받은 예외의 원래 타입은 System.Reflection.ReflectionTypeLoadException이죠. 근데 그 예외를 그냥 Exception 타입으로 받아와서 ReflectionTypeLoadException이 가지고 있던 고유의 데이터는 다음과 같이 숨겨져 버린거죠.


아;; 친절하게도 무려 한글 오류씩이나;;
네, 타입 로드에 실패한 원인은 System.Windows.Controls.Data 어셈블리가 미리 로드 되지 않아서였군요.
컴파일 시점에는 드러나지 않았던 의존 관계라서 디버깅에 참 애를 먹었어요.

요즘 Prism 4 + MEF로 프로젝트 진행중인데 동적 로딩을 많이 사용해서 생각지도 못한 에러가 많아요.
그나마 비주얼 스튜디오가 아니었으면 이런 에러는 어떻게 잡나 싶을 정도로요.
어쨌든!

Don't Panic!
저작자 표시 동일 조건 변경 허락
신고
Posted by gongdo


티스토리 툴바