Что такое метод MEF, чтобы получить существующее экспортированное значение / объект (не получить или создать)? - PullRequest
5 голосов
/ 18 марта 2012

Вот как бы я хотел, чтобы это работало

container.GetButDoNotCreate<T>(); // should throw (or can return null) if container doesn't contain an instance matching the contract

var x = container.GetExportedValue<T>();

var y = container.GetButDoNotCreate<T>(); // should return the object created in previous step

Assert.That(x).IsSameAs(y);

Разница в том, что этот метод не будет создавать экземпляр, если контейнер не содержит его. Это чисто-получи, т.е. достань мне этот объект, если он существует в контейнере. Мне это нужно для моих тестов, где я не хочу, чтобы тестовый код создавал объекты в производственном контейнере (если они не созданы), просто использовал существующие. Только рабочий код должен добавлять / удалять объекты в контейнере.

Опубликовано на MEF codeplex forum но ответов нет. Так что надеюсь, что кто-то на SO может иметь ответ. Кроме того, если бы мне нужно было написать эту функцию как метод расширения ... это было бы неплохо и в качестве ответа.

Ответы [ 2 ]

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

Вот мое решение.Сначала я попытался создать метод расширения.Я попробовал несколько вещей, прочитал документацию и изучил доступные свойства, события и методы контейнера и каталога, но ничего не смог сделать.

Подумав о проблеме, единственный способ найти решение - создать производный контейнер, основанный на CompositionContainer и реализующий метод GetButDoNotCreate.

Обновление:После публикации я понял, что решение работает только в простом примере, который вы опубликовали, где только GetExportedValue используется для извлечения деталей из простых деталей.Если вы не используете контейнер как простой сервисный локатор для деталей без [Import], которые не будут работать при создании деталей с [Import].

Вот реализация:

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;

namespace GetButNotCreate
{
    public class CustomContainer : CompositionContainer
    {
        private List<Type> composedTypes = new List<Type>();

        public CustomContainer(ComposablePartCatalog catalog)
            : base(catalog) 
        {
        }

        public new T GetExportedValue<T>()
        {
            if (!composedTypes.Contains(typeof(T)))
                composedTypes.Add(typeof(T));

            return base.GetExportedValue<T>();
        }

        public T GetButDoNotCreate<T>()
        {
            if (composedTypes.Contains(typeof(T)))
            {
                return base.GetExportedValue<T>();
            }

            throw new Exception("Type has not been composed yet.");
        }
    }
}

Он работает, переопределяя метод GetExportedValue для отслеживания типов, которые были составлены до сих пор, а затем использует это для проверки композиции типов в GetButNotCreate.Я бросил исключение, как вы упомянули в своем вопросе.

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

Вот модульные тесты, которые тестируют новый метод:

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

namespace GetButNotCreate
{
    public interface IInterface { }

    [Export(typeof(IInterface))]
    public class MyClass : IInterface 
    {
    }

    [TestClass]
    public class UnitTest1
    {      
        [TestMethod]
        [ExpectedException(typeof(Exception), "Type has not been composed yet.")]
        public void GetButNotCreate_will_throw_exception_if_type_not_composed_yet()
        {
            var catalog = new AssemblyCatalog(typeof(UnitTest1).Assembly);
            CustomContainer container = new CustomContainer(catalog);
            container.ComposeParts(this);

            var test = container.GetButDoNotCreate<IInterface>();
        }

        [TestMethod]
        public void GetButNotCreate_will_return_type_if_it_as_been_composed()
        {
            var catalog = new AssemblyCatalog(typeof(UnitTest1).Assembly);
            CustomContainer container = new CustomContainer(catalog);
            container.ComposeParts(this);

            var x = container.GetExportedValue<IInterface>();
            var y = container.GetButDoNotCreate<IInterface>();

            Assert.IsNotNull(y);
            Assert.AreEqual(x, y);
        }
    }
}

Это показывает, что GetButNotCreate выдает исключение, еслитип никогда не экспортировался, и он вернет этот тип, если он уже был импортирован.

Я нигде не мог найти хуков, чтобы проверить (не прибегая к рефлексии), чтобы увидеть, состоит ли MEF из какой-либо части, так что это решение CustomContainer было бы моим лучшим выбором.

0 голосов
/ 27 марта 2012

Я думаю, что стоит иметь свою собственную оболочку над контейнером. Что-то вроде IContainerWrapper и используйте его везде в своем коде.

Имея это на месте:

  • Если у вас есть обычные юнит-тесты, вы можете просто добавить другой экземпляр оболочки, который соответствует вашим потребностям.

  • Если вы хотите получить доступ к производственному контейнеру, но у вас есть поведение, описанное выше, вы можете использовать директиву препроцессора в вашей реализации производственной оболочки.

...