Посочете 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 или някой от неговите атрибути за импортиране и трябва да използва моя IoC API, за да получи екземпляри, експортирани типове. Имам нужда от начин да посоча CreationPolicy, когато използвам програмно CompositionContainer за GetExports и GetExportedValues. Това дори възможно ли е без използване на атрибути за импортиране?


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


Отговори (2)


Ако наистина искате да направите заявка към контейнера точно както ако имате ImportAttribute с RequiredCreationPolicy=NonShared, опитайте да създадете свой собствен персонализиран ContractBasedImportDefinition. Един от параметрите за конструктора е CreationPolicy, който представлява необходимата политика за създаване. Нещо като:

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

Разбира се, можете да коригирате параметрите според нуждите, но това ще ви накара да се движите в правилната посока и ще накара контейнера да създаде несподелени версии на всяка част, която е маркирана като Any (което е по подразбиране).

person Wes Haggard    schedule 11.10.2011
comment
Благодаря, Уес, това прави точно това, което търся. - person Xacron; 11.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);
person Matthew Abbott    schedule 10.10.2011
comment
Матей, благодаря за бързия отговор. Това, което представихте, изглеждаше обещаващо, но не мисля, че работи, както бихте очаквали. „CreationPolicy“ по подразбиране всъщност е „Any“, това е, което MEF използва, когато при експортиране не е посочена PartCreationPolicy. Въпреки това, когато при импортиране не е посочена RequiredCreationPolicy, MEF по подразбиране използва „Споделено“. Така че проблемът би бил, когато извикам „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 да се държи като атрибут за импортиране, указващ 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