C# 4.0 为非属性化导入指定所需的CreationPolicy

C# 4.0 为非属性化导入指定所需的CreationPolicy,c#-4.0,inversion-of-control,mef,C# 4.0,Inversion Of Control,Mef,我有一个使用MEF作为DI容器的IoC包装器,下面显示了一个适用的包装器片段 public static bool TryGetComponent<T>(out T component) { CompositionContainer container = RetrieveContainer(); T retrievedComponent = container.GetExportedValueOrDefault<T>(); if (retri

我有一个使用MEF作为DI容器的IoC包装器,下面显示了一个适用的包装器片段

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;
}
对于我创建的类型,我可以很容易地使用以下import属性来获取MEF,以便将导出的类型作为非共享实例提供服务

[Import(RequiredCreationPolicy = CreationPolicy.NonShared)]

但是,由于我的IoC包装器也必须由不使用MEF或其任何导入属性的类使用,并且必须使用我的IoC API来获取导出类型的实例当我以编程方式使用CompositionContainer获取Exports和GetExportedValues时,我需要一种方法来指定CreationPolicy。甚至不使用导入属性也可以这样做吗?

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

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

如果您确实希望查询容器,就像您有一个RequiredCreationPolicy=NonShared的ImportAttribute一样,那么请尝试创建您自己的自定义属性。构造函数的参数之一是表示所需创建策略的CreationPolicy。 比如:

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

当然,您可以根据需要调整参数,但这将使您朝着正确的方向移动,并将导致容器创建标记为任意(默认)的任何零件的非共享版本.

如果您确实希望像查询具有RequiredCreationPolicy=NonShared的ImportAttribute一样查询容器,请尝试创建您自己的自定义属性。构造函数的参数之一是表示所需创建策略的CreationPolicy。 比如:

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

当然,您可以根据需要调整参数,但这将使您朝着正确的方向前进,并将导致容器创建标记为任意(默认)的任何零件的非共享版本。

Matthew,感谢您的快速响应。你所展示的看起来很有希望,但我认为它并不像人们所期望的那样有效。默认的“CreationPolicy”实际上是“Any”,这是MEF在导出上未指定PartCreationPolicy时使用的。但是,如果在导入上未指定RequiredCreationPolicy,则MEF默认使用“共享”。所以问题是,当我调用“container.GetExportedValueOrDefault(CreationPolicy.NonShared)”时,即使导出没有指定“Shared”,也不会有元数据匹配,也不会返回任何部分;不是预期的行为。@Steve-啊,我不知道[可能会被RequiredCreationPolicy和PartCreationPolicy行为弄糊涂]。将会更新。@Steve-这样做更好吗?更新为假定
Any
,并将满足未提供元数据的任何部分?Matthew,感谢更新。当RequiredCreationPolicy为非共享且PartCreationPolicy为Any时,这当然会返回一个值,但是,我意识到,要使此API像指定RequiredCreationPolicy为非共享的导入属性一样工作,还有很多工作要做:当PartCreationPolicy为Any时,有些调用方可能希望共享实例,有些则可能不希望。希望共享的实例应该都获得相同的实例(保留对Lazy的引用并始终使用其值)。对于所有其他调用方,每次都必须使用一个新的Lazy。Matthew,我意识到的另一个问题是,使用Import属性且RequiredCreationPolicy为Shared的对象将不会获得与调用此扩展方法并指定CreationPolicy为Shared的对象相同的共享实例。因为MEF将使用它自己的共享内部惰性实例,而扩展方法将使用一个不同的共享惰性实例。Matthew,感谢您的快速响应。你所展示的看起来很有希望,但我认为它并不像人们所期望的那样有效。默认的“CreationPolicy”实际上是“Any”,这是MEF在导出上未指定PartCreationPolicy时使用的。但是,如果在导入上未指定RequiredCreationPolicy,则MEF默认使用“共享”。所以问题是,当我调用“container.GetExportedValueOrDefault(CreationPolicy.NonShared)”时,即使导出没有指定“Shared”,也不会有元数据匹配,也不会返回任何部分;不是预期的行为。@Steve-啊,我不知道[可能会被RequiredCreationPolicy和PartCreationPolicy行为弄糊涂]。将会更新。@Steve-这样做更好吗?更新为假定
Any
,并将满足未提供元数据的任何部分?Matthew,感谢更新。当RequiredCreationPolicy为非共享且PartCreationPolicy为Any时,这当然会返回一个值,但是,我意识到,要使此API像指定RequiredCreationPolicy为非共享的导入属性一样工作,还有很多工作要做:当PartCreationPolicy为Any时,有些调用方可能希望共享实例,有些则可能不希望。希望共享的实例应该都获得相同的实例(保留对Lazy的引用并始终使用其值)。对于所有其他调用方,每次都必须使用一个新的Lazy。Matthew,我意识到的另一个问题是,使用Import属性和RequiredCreationPolicy为Shared的对象将不会获得与对象相同的共享实例