Как получить экспортированные значения (MEF) в приложениях в стиле метро

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

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


person Enyra    schedule 01.03.2012    source источник
comment
Вы нашли решение? Я остановился на ExportFactory и SatisfyImportsOnce, но меня это не устраивает, мне нужно просто GetExportedValues   -  person ie.    schedule 14.03.2012
comment
Я связался с одним из парней, разрабатывающих MEF для приложений в стиле метро. Он сказал мне, что SatisfyImportsOnce в настоящее время является единственным способом составления элементов. Он спросил меня, какие конкретные сценарии я имею в виду, которые не поддерживаются SatisfyImportOnce: social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/ На данный момент я нашел крошечную альтернативу MEF: metroioc.codeplex.com Надеюсь, это также поможет вам.   -  person Enyra    schedule 14.03.2012
comment
Я не хочу использовать другие фреймворки, так как хочу делиться кодом между проектами. и поскольку я уже использую MEF в исходном проекте, я хочу продолжать его использовать. Мои потребности не слишком велики, поэтому я предпочитаю добавлять недостающую функциональность. Я добавлю свое текущее решение в качестве ответа.   -  person ie.    schedule 14.03.2012


Ответы (2)


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

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;
        }
    }
}

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

person ie.    schedule 14.03.2012
comment
Это не плохой подход! Жалко только, что такой код приходится писать самим :( Спасибо, чувак. - person Enyra; 14.03.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, но я, честно говоря, в настоящее время не уверен).

person DevTheo    schedule 02.03.2012
comment
Привет, DevTheo, это я уже понял, к сожалению, это не совсем то, что я ищу. Контейнер позволял получить конкретное значение экспорта, не выполняя никаких операций импорта: container.GetExportedValue‹IServiceLocator›(); Я ищу аналогичную функцию в Metro MEF. - person Enyra; 04.03.2012
comment
Извините, я не прибил это для вас ... Я не мог найти именно то, что вы хотели (и, как я сказал, нужно вернуться к работе с клиентами ‹ухмылка/›).. Если у меня будет шанс, я поищу немного дальше.. У меня есть другая идея, но мне нужно кое-что поэкспериментировать, прежде чем я скажу, что она у меня есть.. - person DevTheo; 05.03.2012
comment
Я не ожидаю, что вы прибьете это за меня ;) У меня уже есть идея, как я могу это сделать, но у меня пока недостаточно времени ^^ Но я не думаю, что это можно сделать без специального кода. - person Enyra; 05.03.2012