Exports and Metadata
Export 에 Metadata 를 등록하고 제어하는 방법입니다. 지난 포스트에서 알 수 있듯이 Export 는 구성 요소간에 Contact 를 제공하여 이들을 구성(Composition) 할 수 있는 플러그인 모델(Plugin Model) 을 제공해 줍니다.
하지만 Contract 가 제공되어 구성 요소를 확장하고 구성하는 것은 매우 용이하다는 것을 알게 되었으나 Contract 로 인해 파생된 다양한 구성 요소를 어떻게 제어하느냐의 고민을 하게 됩니다. 즉, 다양한 구성 요소 가운데 내가 필요로 하는 구성 요소를 골라낼 수 있도록 Metadata 를 제공하여 구성 요소를 질의(Query)할 수 있도록 하는 것입니다.
예를 들어 아래와 같은 Export 구성 요소 중 어떻게 EmailMessageSender 로 Say() 를 호출할 것인가가 문제인 것이죠.
[Export(typeof(IMessageSender))]
public class EmailMessageSender : IMessageSender
{
public void Say()
{
Console.WriteLine("Import EmailMessageSender");
}
}
[Export(typeof(IMessageSender))]
public class PhoneMessageSneder : IMessageSender
{
public void Say()
{
Console.WriteLine("Import PhoneMessageSneder");
}
}
[Export(typeof(IMessageSender))]
public class SmsMessageSender : IMessageSender
{
public void Say()
{
Console.WriteLine("Import SmsMessageSender");
}
} |
이런 경우, 원초적인 방법으로 리플랙션(Reflection) 을 이용하여 Type 검사를 통해 EmailMessageSender 를 골라내서 사용하면 되지만, 직접적으로 Type 을 검사하기 위해서는 Tightly Coupling 이 발생하여 결국 유연한 플러그인 모델을 구현하기 위해 아무런 도움이 되지 않습니다.
이러한 방법을 해소하기 위해 또 다른 우회 방법은 리플랙션을 통해 Modules Name 으로 비교하는 방법이지만, 이것 또한 플러그인 모델에서 구성 요소가 교체 되었을 경우를 생각하면 전혀 대응할 수 없는 방법입니다.
MEF 에서는 이런 문제를 해소하기 위해 Contract 에 Metadata 를 제공하며 정적/동적인 방법을 제공해 줍니다.
Attaching Metadata to an Export
Export 에 Metadata 를 제공해 주기 위해서 MEF 에서는 ExportMetadataAttribute 특성을 제공해 줍니다.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Field,
AllowMultiple = true, Inherited = false)]
public ExportMetadataAttribute(string name, object value)
왜 ExportMetadata 클래스는 sealed 로 선언이 되었나요?
일반적으로 sealed 로 클래스를 봉인할 경우 리플랙션의 성능이 향상됩니다.
|
ExportMetadata 는 키와 값(Key/Value) 을 지정하여 Contract 에 Metadata 를 제공해 줄 수 있습니다. 그리고 하나의 Export 에 여러 개의 Metadata 를 제공할 수 있도록 AllowMultiple 을 지원합니다.
Metadata 는 여러 가지의 정보를 포함할 수 있습니다. Export 가 제공하는 기능의 특성을 기술할 수 있으며, 예를 들어, 권한, 로깅, 구성 요소 분류 방법 등이 될 수 있을 것입니다.
아래의 소스 코드는 Metadata 를 지정하는 예를 보여줍니다.
[Export(typeof(IMessageSender))]
[ExportMetadata("SenderType", "Email")]
[ExportMetadata("Logging", true)]
public class EmailMessageSender : IMessageSender
{
public void Say()
{
Console.WriteLine("Import EmailMessageSender");
}
}
[Export(typeof(IMessageSender))]
[ExportMetadata("SenderType", "Phone")]
public class PhoneMessageSneder : IMessageSender
{
public void Say()
{
Console.WriteLine("Import PhoneMessageSneder");
}
}
[Export(typeof(IMessageSender))]
[ExportMetadata("SenderType", "Sms")]
public class SmsMessageSender : IMessageSender
{
public void Say()
{
Console.WriteLine("Import SmsMessageSender");
}
}
|
Constraining Imports statically
MEF Preview 5 에서는 ImportRequiredMetadataAttribute 클래스가 제거되었습니다.
MEF Preview 4 에서는 선언적인 방법으로 ImportRequiredMetadataAttribute 를 통해 Metadata 를 질의할 수 있었으나, MEF Preview 5 에서는 ImportRequiredMetadataAttribute 클래스가 제거되었습니다.
아마도 추측으로는 ImportRequiredMetadataAttribute 를 선언 시에 여러 개의 구성 요소가 검색될 경우 Exception 이 발생하는데, Exception 을 최소화 하고자 제거가 된 것 같습니다.
혹시 Statically 한 방법으로 ImportRequiredMetadataAttribute 에 대응되는 클래스를 아시면 저에게 알려주세요. |
Constraining Imports dynamically
이 방법은 Export 의 ExportMetadata 를 런타임 시에 질의(Query) 하는 방법입니다.
Import 시 ExportCollection<T> 을 사용하여 Export 를 수동적으로 질의(Query) 하는 방법입니다. 이 방법은 지난 포스트의 Lazy Load 를 이용한 방법으로 단지 Metadata 만 질의(Query) 뿐이고, 객체의 생성에 대한 판단은 필요 시에만 GetExportedobject() 메서드를 이용하여 생성할 수 있습니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
namespace MetadataSample
{
class Program
{
[Import(typeof(IMessageSender))]
ExportCollection<IMessageSender> Sender { get; set; }
static void Main(string[] args)
{
Program program = new Program();
program.Run();
foreach (var export in program.Sender)
{
if ((string)export.Metadata["SenderType"] == "Email")
export.GetExportedObject().Say();
if (export.Metadata.ContainsKey("Logging") &&
(bool)export.Metadata["Logging"] == true)
Console.WriteLine("Logged success");
}
}
void Run()
{
var catalog = new AggregateCatalog(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
var container = new CompositionContainer(catalog);
var batch = new CompositionBatch();
batch.AddPart(this);
container.Compose(batch);
}
public interface IMessageSender
{
void Say();
}
[Export(typeof(IMessageSender))]
[ExportMetadata("SenderType", "Email")]
[ExportMetadata("Logging", true)]
public class EmailMessageSender : IMessageSender
{
public void Say()
{
Console.WriteLine("Import EmailMessageSender");
}
}
[Export(typeof(IMessageSender))]
[ExportMetadata("SenderType", "Phone")]
public class PhoneMessageSneder : IMessageSender
{
public void Say()
{
Console.WriteLine("Import PhoneMessageSneder");
}
}
[Export(typeof(IMessageSender))]
[ExportMetadata("SenderType", "Sms")]
public class SmsMessageSender : IMessageSender
{
public void Say()
{
Console.WriteLine("Import SmsMessageSender");
}
}
}
}
|
실행 결과는 예상할 수 있듯이 아래와 같이 런타임 시에 결과를 보여줍니다.
'Managed Extensibility Framework' 카테고리의 다른 글
[MEF] 9. Recomposition (1) | 2009.04.19 |
---|---|
[MEF] 8. Strongly Typed Metadata (0) | 2009.04.16 |
[MEF] 6. Lazy Exports (0) | 2009.04.13 |
[MEF] 5. Catalog 사용 (0) | 2009.04.09 |
[MEF] 4. Import 선언 (0) | 2009.04.07 |