开发者

Specify RequiredCreationPolicy for non-Attributed Imports

开发者 https://www.devze.com 2023-04-12 04:16 出处:网络
I have an IoC wrapper that uses MEF as it\'s DI container, an applicable snippet of the wrapper is shown below.

I have an IoC wrapper that uses MEF as it's DI container, an applicable snippet of the wrapper is shown below.

public static bool TryGetComponent<T>(out T c开发者_JAVA百科omponent) 
{
    CompositionContainer container = RetrieveContainer();

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

    component = retrievedComponent;

    return true;
}

Most of the exported components in the CompositionContainer specify a CreationPolicy of "Any".

[PartCreationPolicy(CreationPolicy.Any)]

For types that I create I can easily use the following import attribute to get MEF to serve the exported types as NonShared instances.

[Import(RequiredCreationPolicy = CreationPolicy.NonShared)]

However, since my IoC wrapper must also be used by classes that do not use MEF or any of its Import attributes and must use my IoC API to obtain instances exported types. I need a way to specify the CreationPolicy when I programmatically use the CompositionContainer to GetExports and GetExportedValues. Is this even possible without using import attributes?


If you really want to query the container exactly like as if you had a ImportAttribute with RequiredCreationPolicy=NonShared then try creating your own custom ContractBasedImportDefinition. One of the parameters for to the contructor is a CreationPolicy that represents the required creation policy. Something like:

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

Of course you can adjust the parameters as necessary but this will get you moving in the right direction and will cause the container to create NonShared versions of any part that is marked as Any (which is the default).


Well, CreationPolicy is passed as part of a component's metadata. This means, you should be able to query the metadata for the part, and see if it exists. The way CreationPolicy is specified in metadata is to use the full type name System.ComponentModel.Composition.CreationPolicy as the key, and the enum result as the value. So, knowing this we can build an extension method:

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

Now, firstly we create our expected key, and then we grab a Lazy<T, TMetadata> instance which includes the type and any associated metadata as a Lazy<T, IDictionary<string, object>> instance. If the lazy comes back as null, we can fail early because there were no matching parts at all.

Next, we can check the metadata dictionary Lazy.Metadata to determine if the metadata exists. If it does, we need to cast and compare against our chosen metadata. If that succeeds, return our part instance.

If that doesn't succeed (e.g., if the part is using the implicit CreationPolicy of Any [i.e., the PartCreationPolicyAttribute is omitted from the export]), we'll assume that the part can be returned, as we can match on the default Any creation policy, so we can match both NonShared and Shared parts.

You should be able to use this in place of the normal GetExportedValueOrDefault<T> call:

T retrievedComponent = container.GetExportedValueOrDefault<T>(CreationPolicy.NonShared);
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号