Получение устаревших объектов для импорта, когда они не относятся к экспорту в MEF - PullRequest
3 голосов
/ 09 января 2010

Я начинаю использовать MEF для создания приложения на основе плагинов и медленно добавляю MEF к миксу. Существует много существующего кода, который еще не имеет ДНК MEF, но я все еще хочу вставить этот код в новые объекты, которые автоматически создаются композицией.

Давайте сделаем этот бетон.

У меня есть список объектов, которые реализуют интерфейс IFoo и работают с моделью приложения определенными, но полезными способами.

interface IFooCool : IFoo {}

class FooCool : IFooCool {...}

interface IFooAwesome : IFoo {}

class FooAwesome : IFooAwesome {}

IEnumerable<IFoo> fooCollection = ProvidedTheOldFashionWay(not, yet, MEF);

Теперь я хочу создать несколько полезных инструментов, которые отображают интерфейсы IFooX на различные действия пользователя, такие как команды меню или нажатия кнопок.

[Export(ITool)]
class CoolTool : ITool
{
    IFooCool _fooCool;
    [ImportingConstructor]
    CoolTool(IFooCool fooCool) 
    {
        _fooCool = fooCool;
    }

    [Export(MenuAction)]
    void DoSomething() { _fooCool.SomeWork(...); }
}

Вот что я хотел бы сделать:

var batch = new CompositionBatch();
foreach(var foo in fooCollection)
{
    batch.AddPart(foo);  //add those legacy objects to the batch
}

var catalog = new TypeCatalog(typeof(CoolTool));  //or assembly or directory, ...
var container = new CompositionContainer(catalog);

container.Compose(batch);

Будет создан экземпляр CoolTool и ему будет передан устаревший объект FooCool. Затем я могу получить экспортированные функции и красиво отобразить их в меню, и мы поехали. Когда пользователь щелкает пункт меню, новый CoolTool будет использовать существующую функциональность интерфейса IFooCool, чтобы сделать что-то, ну, круто.

Конечно, это не работает. Поскольку устаревшие объекты не относятся к экспорту, добавление их в пакет композиции не помогает. В приведенном выше коде я добавляю экземпляры foo в пакет с batch.AddPart(object) вместо batch.AddPart(ComposablePart). Первый метод использует атрибутированную модель для обнаружения компонуемой информации от объекта.

Как я могу использовать вторую перегрузку? Могу ли я обернуть свой существующий объект не MEF в ComposablePart на лету? Что-то вроде:

batch.AddPart(CreateComposablePart(typeof(IFooCool), foo));

Кстати, я использую предпросмотр 8 в приложении без Silverlight.

Ответы [ 3 ]

1 голос
/ 09 января 2010

Похоже, вы пытаетесь вызвать этот метод расширения:

AttributedModelServices.AddExportedValue<T>(
   this CompositionBatch batch,
   string contractName,
   T exportedValue);

Ваша проблема в том, что вы знаете параметр типа T только во время выполнения; это ключ вашего словаря. Одним из решений может быть использование отражения для вызова метода, который позволяет заполнять параметры типа во время выполнения. Во-первых, получите MethodInfo обобщенного метода, подобного этому:

MethodInfo genericAddExportedValue = 
   typeof(AttributedModelServices).GetMethods()
   .Where(x=>x.Name == "AddExportedValue")
   .First(x=>x.GetParameters().Count() == 3);

Теперь вы можете закрыть параметр type внутри вашего цикла и вызвать метод:

foreach(var entry in _registeredFoos)       
{       
    MethodInfo addExportedValue = 
       genericAddExportedValue.MakeGenericMethod(entry.Key);
    addExportedValue.Invoke(
       null,
       new object[] {batch, entry.Key.FullName, entry.Value});
}

В качестве альтернативы вы можете создать реализацию абстрактного класса ExportProvider, который знает, как использовать ваш словарь _registeredFoos для экспорта. Но это, вероятно, гораздо больше работы.

1 голос
/ 09 января 2010

И есть способ - sortof.

batch.AddExportedValue(typeof(IFooCool).Fullname, (IFooCool)foo);

К сожалению, проблема немного сложнее, чем эта.Foo, которые я должен отметить, поскольку экспорт на самом деле в этом:

Dictionary<Type, IFoo> _registeredFoos;

IFooCool      => FooCool
IFooAwesome   => FooAwesome

И, следующее (без IFooCool cast) не работает:

batch.AddExportedValue(typeof(IFooCool).Fullname, foo);

Так что я действительнонадо зациклить foos вот так:

foreach(var entry in _registeredFoos)
{
    batch.AddExportedValue(entry.Key.Fullname, // that was easy...
                          (?)entry.Value);     // what?  This is a generic method...
}

Хорошо, я просто раскрою источник и посмотрю, что происходит.Отличное решение, верно?Знание и использование внутренних деталей функции фреймворка - это всегда полезный способ разработки ваших приложений.Я сделаю это:

foreach(var entry in _registeredFoos)
{
    string typeIdentity = AttributedModelServices.GetTypeIdentity(entry.Key);
    IDictionary<string, object> metadata = new Dictionary<string, object>();
    metadata.Add(CompositionConstants.ExportTypeIdentityMetadataName, typeIdentity);

    Export export = new Export(entry.Key, metadata, () => entry.Value);
    batch.AddExport(export);
}

Конечно.Теперь мне нужно пойти принять душ.

0 голосов
/ 11 января 2010

Чтобы использовать устаревшие классы без атрибутов в качестве частей MEF, вы можете использовать ConfigurableDefinitionProvider , который является частью MEFContrib. Это позволяет вам определять импорт и импорт с помощью файла конфигурации вместо атрибутов.

(Ваш вопрос, проясненный вашим собственным ответом, на самом деле состоит в том, как добавить детали, которые у вас уже есть, как Dictionary<Type,object>, но я подумал, что было бы также интересно ответить на более простой вопрос, предложенный заголовком вопроса.) 1006 *

...