시작하기 전에
 
MEF 는 이미 CodePlex 사이트의 Wiki 에 코드를 중심으로 설명이 잘되어 있습니다. 그렇기 때문에 저도 CodePlex 의 사이트를 참고하여 나름대로 각색하여 작성을 하고자 합니다. 레퍼런스가 이미 CodePlex 에 충분하지만, 저는 나만의 시각에서 바라보고 느낀 바를, 그리고 소스 코드를 만들어 가고자 합니다^^ (사실 Wiki 의 설명은 미약할 다름입니다^^;)
 
CodePlex 의 Wiki 를 먼저 보실 분은 최신 버전이 적용이 되지 않은 예제도 있으니 이런 부분은 조심해서 리뷰하시기 바랍니다.
 
 
어플리케이션에 MEF 호스팅하기
 
[그림1] Composition Container
 
Composable Part
 
MEF 에 어플리케이션을 호스팅하기 위해서는 몇 가지의 반복적인 절차를 거치면 됩니다. 먼저 MEF 를 호스팅할 수 있는 컨테이너(Container) 를 만들어야 합니다. 컨테이너는 MEF 에서 상위 그룹에 존재하며 MEF 의 파트(Part) 를 관리하며 핵심 역할을 담당합니다.
 
MEF 의 컨테이너는 배치(Batch) 를 구조적으로 구성하고 캡슐화 합니다. 배치(Batch) 의 각 구성 단위를 파트(Part) 라고 부릅니다. 각 파트(Part) 는 닷넷 어셈블리에서 타입(Type) 이 될 수 있으며, 타입(Type) 을 배치(Batch) 로 등록할 수 있습니다.
 
파트(Part) 는 메인 호스트(Main Host) 가 반드시 포함되어야 하며, 아래의 소스 코드에서 보는 것처럼 this 키워드가 바로 메인 호스트(Main Host) 가 됩니다.
 
Contract
 
이러한 Composable Part 는 다른 컴포넌트와 의존 관계를 갖지 않습니다. 모든 외부 기능(Export) 는 계약(Contract) 를 맺게되며 이것이 필요할 경우에는 Import 할 수 있습니다.
 
MEF 의 컨테이너는 Contract 정보의 타입 정보나 메타 데이터를 통해 Export 와 Import 를 매칭합니다.
 
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
 
class Program
{
    static void Main(string[] args)
    {
        Program p = new Program();
        p.Run();
    }
 
    public void Run()
    {
        var container = new CompositionContainer();
        var batch = new CompositionBatch();
        batch.AddPart(this);
        container.Compose(batch);
    }
}
 
이러한 파트(Part) 를 제어하는 작업은 어플리케이션 차원에서 유일해야 하므로 스레드(Thread) 작업에 안전하도록 lock 으로 블로킹(Blocking) 되어있습니다.
 
이 작업은 메인 호스트(Main Host) 를 등록하는 과정에 불과하며 실행 시에 아무런 결과가 없습니다.
 
 
외부로 기능 노출하기 (Export)
 
이제 어떤 플러그인(Plugin) 을 외부로 노출해야 합니다. 외부로 노출하는 이유는 내/외부에서 노출된 확장 기능을 가져다 쓰기 위해서입니다. 확장 기능을 노출하기 위해서는 단지 Export Attribute 을 정의해 주면 됩니다.
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
 
namespace MEFLab_Hosting_MEF
{
    class Program
    {
        static void Main(string[] args)
        {
               Program p = new Program();
               p.Run();
        }
 
        public void Run()
        {
               var container = new CompositionContainer();
               var batch = new CompositionBatch();
               batch.AddPart(this);
               container.Compose(batch);
        }
    }
 
    public interface IHelloWorld
    {
        void Say();
    }
 
    [Export(typeof(IHelloWorld))]
    public class HellowWorld : IHelloWorld
    {
        public void Say()
        {
               Console.WriteLine("Hello MEF!");
        }
    }
 
인터페이스(Interface) 를 구현한 HelloWorld 클래스는 Export Attribute 를 정의합니다. Export 의 파라메터는 메타 데이터(Meta Data)로 사용되기 때문에 타입(Type) 을 정의해 주어야 합니다.
 
혹자는 왜 인터페이스를 정의해야 하느냐고 궁금해 하기도 합니다. 정답을 드리자면, 반드시 인터페이스를 정의하지 않아도 됩니다. 하지만 특히 MEF 에서 인터페이스는 다형적이고 표준적인 메타 데이터를 제공하기 위해 인터페이스를 선언하여 사용하는 것이 좋을 것 같네요^^
 
 
외부 기능 가져오기 (Import)
 
Export Attribute 으로 외부로 노출된 확장 기능은 Import Attribute 으로 가져올 수 있습니다. 단, 배치(Batch) 의 파트(Part) 로 등록이 되어 있어야 합니다. 내부적으로 Import 의 타입을 생략할 경우 Export 의 타입의 메타 데이터를 통해 Import 의 개체를 쿼리(Query) 하여 생성하게 됩니다.
 
class Program
{
    static void Main(string[] args)
    {
        Program p = new Program();
        p.Run();
    }
 
    [Import]
    IHelloWorld HelloWorldCompoent { get; set; }
 
    public void Run()
    {
        var container = new CompositionContainer();
        var batch = new CompositionBatch();
        batch.AddPart(new HellowWorld());
        batch.AddPart(this);
        container.Compose(batch);
 
        HelloWorldCompoent.Say();
    }
}
 
public interface IHelloWorld
{
    void Say();
}
 
[Export(typeof(IHelloWorld))]
public class HellowWorld : IHelloWorld
{
    public void Say()
    {
        Console.WriteLine("Hello MEF!");
    }
}
 
이 예제에서 알 수 있듯이 코드에서 모듈(Module) 이나 타입(Type) 의 관계를 크게 신경 쓰지 않았습니다. Export 와 Import 를 통해서 의존(Dependency) 는 어느 정도 해소된 것처럼 보입니다. (인터페이스만 잘 활용하면 MEF 없이도 충분히 가능합니다)
 
이것이 끝이라면 정말 재미가 없겠죠. 차차 나오게 될 내용이지만, 이러한 Export 와 Import 를 활용하고 Lazy Load, 그리고 메타 데이터를 이용한 쿼리(Query) 를 활용하게 되면 복잡한 기능을 획기적으로 단순화 할 수 있습니다.

저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by 땡초 POWERUMC

댓글을 달아 주세요