Как получить экспортированные значения (MEF) в приложениях в стиле metro - PullRequest
3 голосов
/ 01 марта 2012

Я понял, что MEF ограничен для приложений в стиле metro.Контейнера больше нет, так как я могу получить конкретные экспортированные значения, такие как ILogger logger = container.GetExportedValues<ILogger>();?Имеется ли какое-либо учебное пособие, посвященное метро-версии MEF?

Спасибо за помощь, Eny

Ответы [ 2 ]

2 голосов
/ 02 марта 2012

Полагаю, вы обнаружили пространства имен System.CompononentModel.Composition и System.CompononentModel.Composition.Hosting.

Позвольте мне привести вам простой пример (и посмотреть, упускаете ли вы что-нибудь).

Прежде всего, вам нужен компонент для внедрения:

public interface IMefTest
{
   string Message {get;}
}

[Export(typeof(IMefTest))]
public class MefTest: IMefTest
{
   public string Message {get { return "Hello World"; }}
}

Далее необходимо настроить CompositionService (это похоже на контейнер, но не совсем). Мы хотим установить это где-нибудь, с чем можно будет поговорить, так как это будет то место, куда вы звоните, чтобы удовлетворить импорт (подробнее об этом в фрагменте кода после этого).

Я вставил это в файл App.xaml.cs в моем примере проекта:

static System.ComponentModel.Composition.ICompositionService _compositionService = null;
public static System.ComponentModel.Composition.ICompositionService CompositionService
{
    get
    {
        if (_compositionService == null)
            ((App)App.Current).loadCompositionService();
         return _compositionService;
    }
}

private void loadCompositionService()
{
    // Create a catalog where MEF will search for exported parts to plugin
    var catalog = new System.ComponentModel.Composition.Hosting.AssemblyCatalog(GetType().GetTypeInfo().Assembly);
    _compositionService = catalog.CreateCompositionService();
}

Просто немного информации об этом. Мы создали каталог сборки, что означает, что MEF будет запрашивать только эту сборку в поисках экспортируемых типов. Существуют другие типы каталогов, и я считаю, что вы должны объединить 2 или более каталогов вместе.

Хорошо, на моей странице (кстати, я встроил это в пример C # HelloWorld из документации по началу работы на MSDN).

Чтобы получить экземпляр одной из частей в каталоге, вам просто нужно добавить свойство к классу, который вы хотите получить, и добавить атрибут импорта (есть также способы, чтобы он определял аргументы в вашем конструкторе, но я пытаюсь оставаться простым)

[Import]
public IMefTest Tester { get; set; }

В этом примере вам нужно вызвать метод SatisfyImportsOnce службы композиции для этого класса. Я сделал это в конструкторе, и я сделал это так:

if(App.CompositionService != null)
  App.CompositionService.SatisfyImportsOnce(this);

(Проверка, чтобы убедиться, что сервис существует, с моей стороны является излишним .. он должен существовать). После этого шага вы можете использовать свойство Tester.Message в вашем классе. В MEF гораздо больше возможностей. Я надеюсь, что это поможет вам (и если вы пометите меня как ответ, поскольку я потратил драгоценное время на выставление счетов клиентам, чтобы сделать это для вас)

Кроме того, я знаю, что вы хотели просто вызвать контейнер, и он вернул вам экземпляр некоторого типа. Это можно сделать с другими формами MEF, поэтому я полагаю, что это должно быть в состоянии сделать с WinRT, но я честно не уверен в это время).

1 голос
/ 14 марта 2012

Как я уже писал в комментарии, мне не очень нравится этот подход, но это лучшее, что у меня есть до этого момента:

public class CompositionContainer
{
    private readonly CompositionService _service;

    public CompositionContainer(CompositionService service)
    {
        _service = service;
    }

    public T GetExportedValue<T>()
    {
        var factoryProvider = new FactoryProvider<T>();
        _service.SatisfyImportsOnce(factoryProvider);
        return factoryProvider.Factory.CreateExport().Value;
    }

    private class FactoryProvider<T>
    {
        [Import]
        public ExportFactory<T> Factory;
    }
}

и простой вариант использования может быть таким:

class Program
{
    static void Main()
    {
        var catalog = new ApplicationCatalog();
        var container = new CompositionContainer(catalog.CreateCompositionService());
        for (int i = 0; i < 5; i++)
        {
            var dude = container.GetExportedValue<IDude>();
            Console.WriteLine(dude.Say());
        }
    }
    public interface IDude
    {
        string Say();
    }

    [Export(typeof(IDude))]
    public class Eminem : IDude
    {
        private static int _instanceNo;
        private readonly string _phrase;

        public Eminem()
        {
            _instanceNo++;
            _phrase = string.Join(" ", Enumerable.Range(0, _instanceNo)
                .Select(_ => "yo!"));
        }

        public string Say()
        {
            return _phrase;
        }
    }
}

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

...