Укажите RequiredCreationPolicy для импорта без атрибутов

У меня есть оболочка IoC, которая использует MEF в качестве контейнера DI, применимый фрагмент оболочки показан ниже.

public static bool TryGetComponent<T>(out T component) 
{
    CompositionContainer container = RetrieveContainer();

    T retrievedComponent = container.GetExportedValueOrDefault<T>();
    if (retrievedComponent.Equals(default(T)))
    {
        component = default(T);
        return false;
    }

    component = retrievedComponent;

    return true;
}

Для большинства экспортируемых компонентов в CompositionContainer для параметра CreationPolicy задано значение Any.

[PartCreationPolicy(CreationPolicy.Any)]

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

[Import(RequiredCreationPolicy = CreationPolicy.NonShared)]

Однако, поскольку моя оболочка IoC также должна использоваться классами, которые не используют MEF или какие-либо из его атрибутов импорта, и должны использовать мой API IoC для получения экземпляров экспортируемых типов. Мне нужен способ указать CreationPolicy, когда я программно использую CompositionContainer для GetExports и GetExportedValues. Возможно ли это вообще без использования атрибутов импорта?


person Xacron    schedule 10.10.2011    source источник


Ответы (2)


Если вы действительно хотите запрашивать контейнер точно так же, как если бы у вас был ImportAttribute с RequiredCreationPolicy=NonShared, попробуйте создать свой собственный Определение импорта на основе контракта. Одним из параметров конструктора является CreationPolicy, который представляет требуемую политику создания. Что-то типа:

container.GetExports(new ContractBasedImportDefinition(
    AttributedModelServices.GetContractName(type),
    AttributedModelServices.GetTypeIdentity(type),
    null,
    ImportCardinality.ZeroOrMore,
    false,
    false,
    CreationPolicy.NonShared));

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

person Wes Haggard    schedule 11.10.2011
comment
Спасибо, Уэс, это именно то, что я ищу. - person Xacron; 11.10.2011

Итак, CreationPolicy передается как часть метаданных компонента. Это означает, что вы должны иметь возможность запросить метаданные для части и посмотреть, существует ли она. Способ указания CreationPolicy в метаданных заключается в использовании полного имени типа System.ComponentModel.Composition.CreationPolicy в качестве ключа и результата перечисления в качестве значения. Итак, зная это, мы можем построить метод расширения:

public static T GetExportedValueOrDefault<T>(this CompositionContainer container, CreationPolicy creationPolicy)
{
  var metadataKey = typeof(CreationPolicy).FullName;

  var lazy = container.GetExportedValueOrDefault<T, IDictionary<string, object>>();
  if (lazy == null)
    return default(T);

  if (lazy.Metadata.ContainsKey(metadataKey))
  {
    // If the creation policy matches the required, return.
    if (((CreationPolicy)lazy.Metadata[metadataKey]) == creationPolicy) 
      return lazy.Value;
  }
  else
  {
    // Return the value as we assume it satisfies the default CreationPolicy = Any
    return lazy.Value; 
  }

  return default(T);
}

Теперь, во-первых, мы создаем ожидаемый ключ, а затем получаем экземпляр Lazy<T, TMetadata>, который включает тип и любые связанные метаданные в качестве экземпляра Lazy<T, IDictionary<string, object>>. Если ленивый возвращается как null, мы можем потерпеть неудачу раньше, потому что вообще не было подходящих частей.

Затем мы можем проверить словарь метаданных Lazy.Metadata, чтобы определить, существуют ли метаданные. Если это так, нам нужно привести и сравнить с выбранными нами метаданными. Если это удастся, верните экземпляр нашей части.

Если это не удается (например, если часть использует неявную CreationPolicy из Any [т. е. PartCreationPolicyAttribute опущена в экспорте]), мы предполагаем, что часть может быть возвращена, так как мы можем сопоставить по умолчанию Any, поэтому мы можем сопоставить как NonShared, так и Shared части.

Вы должны иметь возможность использовать это вместо обычного вызова GetExportedValueOrDefault<T>:

T retrievedComponent = container.GetExportedValueOrDefault<T>(CreationPolicy.NonShared);
person Matthew Abbott    schedule 10.10.2011
comment
Матвей, спасибо за быстрый ответ. То, что вы представили, выглядело многообещающе, но я не думаю, что это работает так, как можно было бы ожидать. По умолчанию «CreationPolicy» на самом деле «Any», это то, что MEF использует, когда в экспорте не указано PartCreationPolicy. Однако, если при импорте не указано RequiredCreationPolicy, MEF по умолчанию использует «Shared». Таким образом, проблема будет заключаться в том, что когда я вызываю «container.GetExportedValueOrDefault‹T›(CreationPolicy.NonShared)», совпадения метаданных не будет, и никакая часть не будет возвращена, даже если в экспорте НЕ указано «Общий»; не ожидаемое поведение. - person Xacron; 10.10.2011
comment
@Steve - Ах, я не знал, что [вероятно, путают поведение RequiredCreationPolicy и PartCreationPolicy]. Будет обновляться. - person Matthew Abbott; 10.10.2011
comment
@Steve - Это работает лучше? Обновлено для предположения Any и будет удовлетворять любой части, где не предоставлены метаданные? - person Matthew Abbott; 10.10.2011
comment
Мэтью, спасибо за обновление. Это, безусловно, возвращает значение, когда RequiredCreationPolicy имеет значение NonShared, а PartCreationPolicy — Any, однако я понимаю, что необходимо проделать дополнительную работу, чтобы заставить этот API вести себя как атрибут Import, указывающий RequiredCreationPolicy на NonShared: когда PartCreationPolicy — Any, некоторые вызывающие объекты могут захотеть поделиться экземпляром, а некоторые — нет. Те, кто хочет поделиться, должны получить один и тот же экземпляр (сохраняйте ссылку на Lazy‹T› и всегда используйте его значение). Для всех остальных вызывающих абонентов необходимо каждый раз использовать новый Lazy‹T›. - person Xacron; 10.10.2011
comment
Мэтью, я понял, что еще одна проблема, которая может возникнуть, заключается в том, что объекты, использующие атрибут Import с RequiredCreationPolicy Shared, не получат тот же общий экземпляр, что и объекты, вызывающие этот метод расширения и указывающие CreationPolicy Shared. Поскольку MEF будет использовать свой собственный общий внутренний экземпляр Lazy‹T›, а метод расширения будет использовать отдельный общий экземпляр Lazy‹T›. - person Xacron; 10.10.2011