Укажите RequiredCreationPolicy для импорта без атрибутов - PullRequest
1 голос
/ 10 октября 2011

У меня есть оболочка 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 со значением «Любой».,

[PartCreationPolicy(CreationPolicy.Any)]

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

[Import(RequiredCreationPolicy = CreationPolicy.NonShared)]

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

Ответы [ 2 ]

4 голосов
/ 11 октября 2011

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

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

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

1 голос
/ 10 октября 2011

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

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);
...