Как стать автором
Обновить

Комментарии 28

MEF не полноценный контейнер. Рассматривать его как альтернативу не совсем корректно. К тому же с ним не удобно работать когда у вас несколько инстансов одного моудля. Про тестирование вообще молчу.
MEF вполне полноценный контейнер, решающий конкретный класс композиционных задач.
В чем Вы видите его неполноценность?
MEF это фреймворк для разработки плагинов, причем более ориентированный на десктопные приложения.
1. Тестируете как с MEF?
2. Лайфтайм объектов?

Рекомендую почитать msdn.microsoft.com/en-us/magazine/gg650670.aspx
1. А что не так с тестированием, при использовании MEF?
2. Ну, по умолчанию, MEF сохраняет ссылки на созданные объекты, но это легко можно изменить.
1. Как вы тестовый контейнер собираете?
2. Сохранение ссылок это даже не смешно ;) Управление жизненным циклом объекта это одна из основных задач IoC контейнера. Instance per HttpRequest? А на вызов в WebApi или WCF? А вложенные области видимости или вложенные контейнеры? А Instance per tenant? В статье справедливо заметили, что часть функционала пересекается, но при этом эти фреймворки решают разные задачи. Если нужна только самая базовая инъекция зависимостей без всего, то можно использовать MEF как IoC. Но шаг вправо или влево — и он уже становится совершенно не пригодным для этой цели.
1. В unit-тестах контейнер не нужен, а для интеграционных, можно использовать тот же TypeCatalog при настройке контейнера.
2. О целях довольно хорошо говорит его название (Managed Extensibility Framework). :)
1. 0_о? Как код то выглядит? Видел реализации с конструктором без аргументов, в котором используется SatisfyImportsOnce, и для тестов — отдельный конструктор с зависимостями. Получается весьма уродливый код, где в каждом классе сидит ссылка на статичный контейнер.

2. Ну изначальный посыл и был в тему того, что это MEF, а не IoC. А в статье автор частенько ставит между ними знак равенства, что может немного поломать мозги тем, что только начинает увлекательное путешествие ;)
Эээ… А в чём проблема?

    public interface IStrategy {
        int Algorithm(int num);
    }

    public sealed class Strategy : IStrategy {
        public int Algorithm(int num) {
            return num*2;
        }
    }

    public sealed class Calculator {
        private readonly IStrategy _strategy;

        public Calculator(IStrategy strategy) {
            _strategy = strategy;
        }

        public int Calculate() {
            return _strategy.Algorithm(42);
        }
    }

    public static class Program {
        private static void Main(string[] args) {
            var rb = new RegistrationBuilder();
            rb.ForTypesDerivedFrom<IStrategy>().Export<IStrategy>();
            rb.ForType<Calculator>().Export();

            var cc = new CompositionContainer(
                new AggregateCatalog(new AssemblyCatalog(typeof (Program).Assembly, rb)));


            var calc = cc.GetExportedValue<Calculator>();

            Console.WriteLine(calc.Calculate());
            Console.ReadLine();
        }
    }

Потом тестим

    [TestMethod]
    public void TestMethod() {
        var calc = new Calculator(
            Mock.Of<IStrategy>(s => s.Algorithm(It.IsAny<int>()) == 42)
            );
        calc.Calculate().Should().Be(42);
    }
cc.GetExportedValue(); Вот в этом месте проблема. Вы все объекты так получаете?

var builder = new ContainerBuilder();
builder.RegisterInstance(Mock.Of(s => s.Algorithm(It.IsAny()) == 42)).As();
builder.RegisterType().AsSelf()
var container = builder.Build();

[TestMethod]
public void TestMethod()
{
var calc = Container.Resolve();
calc.Calculate().Should().Be(42);
}

PS. Извиняюсь, мне уже отрубили тэги и парсер кушает дженерики :)
На мой взгляд, GetExportedValue должен быть упрятан в глубине инфраструктуры и не появляться в обычных классах приложения, как и другие ссылки на контейнер. С Unity, к примеру, единственное место, где появляется IUnityContainer, это класс модуля (в Prism). Во всех остальных местах в конструктор инжектируются уже необходимые интерфейсы. С MEF даже это становится не нужным, модули регистрируют свои типы автоматически при их загрузке. В unit-тестах, идея использования контейнера вообще очень сомнительна. Все необходимые стабы и моки создаются и инжектируются вручную в эти самые конструкторы. Собственно, для чего это всё и задумывалось.
А wireup то как? Допустим в MVC контроллер? Это все равно натягивание одного на другое. MEF — для плагинов и очень базовой композиции, IoC — для всего остального. И то, при определенных требованиях, MEF не подходит. Просто не надо считать его за полноценный контейнер и сравнивать с Autofac, Windsor, StructureMap и на худой конец Unity.
Передать ссылку на CompositionContainer в активатор контроллеров и через него их ресолвить, как и с обычным контейнером. Проблем-то?
А лайфтайм то как контролировать? Это все самые базовые сценарии. Мы не касаемся еще интерсепторов и тд. С тем же успехом можно и просто везде сервис локатор вкрутить, но вот только он покроет 10% юзкейсов, которые могут возникнуть в сложном приложении. Ради интереса посмотрите Orchard Project и как используется IoC на полную катушку. MEF — только базовая композиция. У него есть свои плюшки, и его вполне можно использовать с IoC контейнером одновременно. Это косяк MS, что они представили в фреймворке технологию, которая частично перекрывает юзкейсы других технологий, но при этом решает совершенно другие задачи.
Подключился поздно, поэтому начну заново — MEF вполне полноценный контейнер, решающий конкретный класс композиционных задач. Если нужно делать хитрые манипуляции c лайфтаймом и перехватывать все этапы построения — юнити, конечно, подходит лучше. Если важнее наглядное, простое и декларативное управление композицией — то лучше, наверное, все-таки меф.

Ну и по вопросам — тестируется все прекрасно, тестируемые объекты складываются в тест-контейнер, дополняются заглушками того, что тестить не надо. Реализация IoC вполне законченная, но на своей практике я встречал людей, которым очень хочется использовать контейнер как фабрику на все случаи жизни и которые требуют от него кучу синтаксического сахара и возможностей, не связанных с композиционным IoC. А это приводит итоге к большой неразберихе.

Еще раз отрезюмирую — для использования с Prism MEF, как композиционный контейнер, подходит едва ли не лучше Unity. Уж точно не хуже. В общем случае надо рассматривать конкретные задачи.
Еще раз: IoC контейнер решает 3 основных задачи — Композиция, Управление жизненным циклом, и, в большинстве своем, поддерживают механизмы перехвата вызова и динамическое проксирование. Выше я привел статью на MSDN где подробно разъясняют что из себя представляет MEF. И, как я написал выше, для простого построения графа объектов в десктопном приложении — MEF подойдет, но при условии что у вас только базовые сценарии использования композиции. Так как MEF без костылей и жуткого колдунства решает только первую задачу, он не является полноценным IoC контейнером и сравнивать его с Autofac, Windsor, StructureMap не корректно, так как они решают разные задачи.
А про Unity я согласен — лучше MEF чем Unity ;-)

Приведу цитату (извиняюсь что без тегов):

In a nutshell, it’s correct to say that the functionality of the MEF and of a typical IoC framework overlap, but don’t coincide. With most IoC frameworks you can perform tasks that the MEF just doesn’t support. You could probably employ a functionally rich IoC container and, with some effort on your own, emulate some MEF-specific capabilities.
Перехват вызовов — это не задача сама по себе, а способ решения какой-либо другой задачи.
MEF обеспечивает внедрение зависимостей (dependency injection) и это именно та характеристика, которая ассоциируется как определяющая для IoC контейнеров, если судить по статьям на хабре и вики. :) Остальное — это уже фантазия тех, кто реализует контейнер. То, что контейнеру можно приделать упомянутый выше перехват не значит, что это обязательная необходимость для IoC контейнера.
Жизненным циклом MEF управляет (т.е. самостоятельно создает и уничтожает объекты определенным политикой образом). И никакого жуткого колдунства и костылей не требуется, чтобы управлять жизненным циклом собственными политиками — хотя писать придется, да.

Таким образом, MEF является вполне полноценным контейнером, также как МАЗ является полноценным автомобилем, пусть и не очень подходящим под то, чтобы на нем на работу ездить.

Статья на MSDN кстати не утверждает, что MEF это не IoC-контейнер. Она говорит, что большинство IoC-фреймворков обладают более богатой функциональностью и можно, немножко дописав, реализовать функциональность MEF (равно как вы утверждаете, что, дописав MEF, можно реализовать функциональность этих фреймворков).

А в целом, за исключением терминологического спора о том, IoC-контейнер ли MEF или нет, мы с вами сходимся во мнении, что задача определяет использование или неиспользование этого самого MEF и с точки приложения к этой задаче сравнивать его можно с любыми другими контейнерами. :)
Там как раз указывается что MEF не обладает той функциональностью, которая есть у большинства IoC контейнеров, и никогда не будет реализована в MEF =)
Когда вышел первый релиз Spring.Net — просто DI было круто и достаточно, но сейчас требования к контейнерам совершенно другие.

Лайфтайм менеджмент в MEF? Как уже выше писал — HttpRequest,WebApiRequest, OperationContext, UnitOfWork — как это все поддерживать? А AOP тоже весьма распространен в разработке серверного ПО. Как это делать с MEF? MEF — это плагины, обновляемые в рантайме каталоги и тд. Это круто, но когда надо автоматически инжектить логгер с именем класса и с возможностью оверрайда этого имени или использовать ленивую инициализацию зависимостей из текущего скоупа выполнения в single instance объект — MEF уже не крут, а возможность делать такие вещи должен предоставить контейнер.

А так да. в десктопном приложении скорее всего MEF. На сервере… видел прототип и как потом его переписывали на Autofac в срочном порядке. Хотя для mission critical софта с длительным циклом поддержки версии я вообще буду использовать свой сервис локатор и сокращать все внешние зависимости :-)

но когда надо автоматически инжектить логгер с именем класса и с возможностью оверрайда этого имени — MEF уже не крут,

Я делал это в MEF. Вполне удобно. Принцип в создании nonshared логгирующего агента и в присвоении агенту имени в событии после импортов. Три понятных строчки кода.
использовать ленивую инициализацию зависимостей из текущего скоупа выполнения в single instance объект — MEF уже не крут

Ну это вообще мне непонятно. Физически один объект разделяется между скопами и каждый скоп хочет видеть в нем свои зависимости?
Если просто разделение объектов между областями — то это в MEF есть.
Лайфтайм менеджмент в MEF?

Думаю о том, чтобы написать по этому поводу статью. Почему бы нет? С созданием и областью видимости проблем не очень много, с ликвидацией проблем больше, хотя последнее, насколько мне известно, проблема не только MEF.

Лично мне нравится то, что MEF не дает собой оперировать как универсальной фабрикой — как, наверное, видно из пред-пред. поста, я считаю это плохой практикой. :)
Приведите пример?=) А как быть с тем что

Ну контейнер то как раз и создан, что бы быть универсальной фабрикой=) Под нужды.
Допустим скользкий момент. когда у вас есть объект с жизненным циклом, равным жизненному циклу контейнера, но вам необходимо в нем получать доступ к объектам, которые имеют более короткий жизненный цикл. И кстати, с ликвидацией то же проблем не будет в нормальном контейнере.

Ну и без AOP вообще не вариант уже жить=) Ну и самое главное, это то, что для MEF необходимо в свой код тащить специфичные для контейнера атрибуты. То есть человек, который использует мой фреймворк вообще не знает, что надо что-то там экспортить и тд. Он просто реализует интерфейсы, а я, в свою очередь, могу ему и логгеры разные давать и доп. сервисы инджектить и сам управлять жизенным циклом объекта(на весь инстанс сервера, на тэнант, на сессию, на запрос). Причем для этого еще могу и контролировать, что он себе инджектит, обвесть это проксями для кеша, аудита, логирования с моей стороны и тд.

Допускаю, что при определенном рвении можно над MEF нарисовать это все, но зачем? Для этого уже есть инструменты, которые на голову превосходят его в этом.

Напишите статью, там и обсудим. Я может то же соберусь с силами и по Autofac напишу. =)
И кстати, с ликвидацией то же проблем не будет в нормальном контейнере.

Да будет, когда неясно, когда и кому объект ликвидировать.
Ну контейнер то как раз и создан, что бы быть универсальной фабрикой=)

Ну вот совершенно не согласен. Я считаю, что смешение в контейнере объектов сервисной категории и объектов, представляющих данные, крайне неполезно. Т.е. не очень хорошо хранить в контейнере соединения с базой, а вот класс, предоставляющий соединения, который композиционно всякое использует — нормально. С таким подходом у меня ограничений даже с мефом не возникало.
Он просто реализует интерфейсы, а я, в свою очередь, могу ему и логгеры разные давать и доп. сервисы инджектить

Самое страшное в AOP в неумелых руках (я не имею в виду вас) — совершенно неочевидные и, подчас, конфликтные инъекции. Собственно, неконтролируемое смешивание всего и вся, как мне кажется, и есть самое серьезное ограничение подобного подхода — ну типа как множественное наследование, только еще менее очевидное. Продуманная архитектура, практически устраняет необходимость в широком применении AOP. А сделать инъекцию со своим временем жизни вполне возможно, ну это уже в статью пойдет. Обвешивать проксями и прочим без сложностей тоже можно, при условии, что написанный пользователем класс позволяет подобные паттерны, и это смотрится вполне естественно в MEF…

Ну и самое главное, это то, что для MEF необходимо в свой код тащить специфичные для контейнера атрибуты.

Совершенно необязательно. Есть класс RegistrationBuilder.
Допускаю, что при определенном рвении можно над MEF нарисовать это все, но зачем?
Многия знания, как говорится, рождают многия печали. :) Обилие возможностей приводит к каше на проекте, когда сложно разобраться, какой объект как получается и что из себя представляет (это, конечно, на больших проектах и в команде чувствуется).
Плюсы мефа я уже указал — простой в использовании, наглядный и надежный вроде как фреймворк для сбора композиции. Зачем пилить полено лазером, если можно пилой?
Соединение с базой в чистом виде вообще вредно просто так хранить в своем коде, а вот Сессии Nhibernate или контексты EF запросто. Причем как раз можно реализовать и менеджмент жизненного цикла, и контроль зависимостей.
А еще абстрактные репозитории, автоматическая инъекция декораторов и стратегий? ;-)

Согласен, AOP и продвинутые практики DI резко повышают планку. Но есть решения как контролировать sanity контейнера и разработчика. Например в Autofac механизм LifetimeScope. Как раз для того, что бы разработчик не смог заресолвить PerRequest в SingleInstance. Тот же аудит, контроль доступа к методу, и даже инджект tenantId как пример=)

Здесь все зависит от сложности проекта, размера команды, и организации процесса=). Мы уже перешли на то, что все компоненты в проектах — Nuget пакеты, стандартизировали подход к DI, и ни у кого не вызывает вопросов. Джуниоры, допустим просто знают что им можно объявить свойство ILogger и им пользоваться. Выход за эти рамки им не нужен.

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

Вот основная моя мысль в чем, которую я пытаюсь донести — вместо того, чтобы думать, как красиво реализовать декораторы и стратегии, лучше думать о том, как обойтись без них. Для этого придумана аббревиатура KISS, например. :) И полнофункциональные в своей области, но не имеющие ничего лишнего за ней средства очень этому способствуют. Зачем думать о том, как контролировать разработчиков, чтобы они в сложности не запутались и не наизобретали лишнего, если можно в принципе этой сложности не допускать?
Опасность опять же шаблонов в том, что, увлекшись кодом, можно забыть о том, что за ним находится.
Причем здесь MEF? :) Он и есть полнофункциональное средство в своей области без всякого лишнего.
А их все равно необходимо контролировать, либо писать тонны повторяющегося кода, кто опять же плохо. Вы забываете про принцип DRY =) И можно нарисовать кучу репозиториев и сервисов и прослоек, либо использовать более сложные техники, но исключить дублирование. MEF — тут не совсем помогает.
Да меф тут не при чем. С точки зрения неповторяемости он помогает так же, как и все остальные контейнеры, только в каких-то случаях (для которых в нем не предусмотрено готового сценария) будет больше кода. Зато в других, за счет того, что в нем меньше возможностей и, соответственно, конфигураций, кода будет меньше.
В каких-то ситуациях будет разумно вообще контейнер не использовать.
«Зато в других, за счет того, что в нем меньше возможностей и, соответственно, конфигураций, кода будет меньше.»

Не совсем понял, как меньшие возможности ведут к уменьшению кода?
Если у меня есть супернавороченная универсальная система, которая может все, но которую надо сконфигурировать, то для решения задачи, которая является родной для более простой системы, решающей непосредственно эту задачу, кода надо писать больше.
Грубо говоря, чем больше у ножика лезвий, тем тяжелее найти нужное. Но это мы уже вообще в глубокую философию ушли. :)
По теме же советую посмотреть на возможности MEF 2 (работа с обобщениями, RegistrationBuilder, дочерние контейнеры). Разумеется, он по-прежнему аскетичен, по сравнению с тем же автофаком например, но, как мне кажется, в 80% приложений его хватит за глаза.
Надо знать что делаешь=) Это самое главное.
Для десктопа — за глаза хватит MEF. Там зачастую нет таких проблем как на сервере.

Кстати, надеюсь парни из MS не будут сильно стучать по голове… посмотрите как TFS2012 написан ;) у них код не обфусцирован… Интересное чтиво.
В MEF есть дочерние контейнеры (если вы это имели в виду под вложенностью).
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории