MEF:有什么方法可以在 class 构建之前有条件地 select 导出?
MEF: Any way to conditionally select an export prior to class construction?
我有两个 ISchedulerProvider
接口的实现,一个用于测试目的,一个用于非测试执行(Lee Campbell 的 Intro to Rx 的赞美)。我想通过 MEF 导出这两个实例并处理导入,如果我检测到我在测试环境中,则使用测试调度程序,如果我不在,则使用另一个。
我可以在每次导出时设置元数据,在任何地方使用 [ImportMany]
我使用 ISchedulerProvider
和相应的过滤器,但这看起来开销很大。在 class 构建之前,有条件地 select 出口之间有什么办法吗?
一个简单的例子会很有帮助,特别是如果解决方案涉及出口供应商之类的东西。
我能够通过创建一个继承自 CompositionContainer 的 class 来实现这一点,它根据自定义属性过滤掉导出。
这是我拼凑的一个例子:
要实现的接口
public interface ISchedulerProvider
{
string Foo { get; }
}
用于保存目标环境信息的自定义属性,我在此示例中使用了字符串,但我可能会在生产代码中使用枚举:
public class EnvironmentSpecificAttribute : Attribute
{
public string TargetEnvironment { get; }
public EnvironmentSpecificAttribute(string targetEnvironment)
{
TargetEnvironment = targetEnvironment;
}
}
示例实现:
[EnvironmentSpecific("QA")]
[Export(typeof(ISchedulerProvider))]
public class TestProvider : ISchedulerProvider
{
public string Foo { get; } = "TestBar";
}
[EnvironmentSpecific("Prod")]
[Export(typeof(ISchedulerProvider))]
public class RealProvider : ISchedulerProvider
{
public string Foo { get; } = "RealBar";
}
Class 消费接口:
[Export]
public class Consumer
{
private readonly ISchedulerProvider _schedulerProvider;
[ImportingConstructor]
public Consumer(ISchedulerProvider schedulerProvider)
{
_schedulerProvider = schedulerProvider;
}
public string GetFoo()
{
return _schedulerProvider.Foo;
}
}
自定义组合容器:
public class EnvironmentSpecificContainer : CompositionContainer
{
private readonly string _targetEnvironment;
public EnvironmentSpecificContainer(ComposablePartCatalog catalog, string targetEnvironment, params ExportProvider[] providers) : base(catalog, providers)
{
_targetEnvironment = targetEnvironment;
}
protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
{
return base.GetExportsCore(definition, atomicComposition)?.Where(IsForEnvironmentOrEnvironmentNotSpecified);
}
private bool IsForEnvironmentOrEnvironmentNotSpecified(Export export)
{
EnvironmentSpecificAttribute attribute = export.Value.GetType().GetCustomAttribute<EnvironmentSpecificAttribute>() as EnvironmentSpecificAttribute;
return attribute == null || string.Equals(attribute.TargetEnvironment, _targetEnvironment,
StringComparison.InvariantCultureIgnoreCase);
}
}
最后是上面classes的用法:
static void Main(string[] args)
{
string environmentName = "QA";
EnvironmentSpecificContainer container = new EnvironmentSpecificContainer(new AssemblyCatalog(Assembly.GetCallingAssembly()), environmentName);
var tmp = container.GetExportedValue<object>("MefTest.Consumer");
}
我有两个 ISchedulerProvider
接口的实现,一个用于测试目的,一个用于非测试执行(Lee Campbell 的 Intro to Rx 的赞美)。我想通过 MEF 导出这两个实例并处理导入,如果我检测到我在测试环境中,则使用测试调度程序,如果我不在,则使用另一个。
我可以在每次导出时设置元数据,在任何地方使用 [ImportMany]
我使用 ISchedulerProvider
和相应的过滤器,但这看起来开销很大。在 class 构建之前,有条件地 select 出口之间有什么办法吗?
一个简单的例子会很有帮助,特别是如果解决方案涉及出口供应商之类的东西。
我能够通过创建一个继承自 CompositionContainer 的 class 来实现这一点,它根据自定义属性过滤掉导出。
这是我拼凑的一个例子:
要实现的接口
public interface ISchedulerProvider
{
string Foo { get; }
}
用于保存目标环境信息的自定义属性,我在此示例中使用了字符串,但我可能会在生产代码中使用枚举:
public class EnvironmentSpecificAttribute : Attribute
{
public string TargetEnvironment { get; }
public EnvironmentSpecificAttribute(string targetEnvironment)
{
TargetEnvironment = targetEnvironment;
}
}
示例实现:
[EnvironmentSpecific("QA")]
[Export(typeof(ISchedulerProvider))]
public class TestProvider : ISchedulerProvider
{
public string Foo { get; } = "TestBar";
}
[EnvironmentSpecific("Prod")]
[Export(typeof(ISchedulerProvider))]
public class RealProvider : ISchedulerProvider
{
public string Foo { get; } = "RealBar";
}
Class 消费接口:
[Export]
public class Consumer
{
private readonly ISchedulerProvider _schedulerProvider;
[ImportingConstructor]
public Consumer(ISchedulerProvider schedulerProvider)
{
_schedulerProvider = schedulerProvider;
}
public string GetFoo()
{
return _schedulerProvider.Foo;
}
}
自定义组合容器:
public class EnvironmentSpecificContainer : CompositionContainer
{
private readonly string _targetEnvironment;
public EnvironmentSpecificContainer(ComposablePartCatalog catalog, string targetEnvironment, params ExportProvider[] providers) : base(catalog, providers)
{
_targetEnvironment = targetEnvironment;
}
protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
{
return base.GetExportsCore(definition, atomicComposition)?.Where(IsForEnvironmentOrEnvironmentNotSpecified);
}
private bool IsForEnvironmentOrEnvironmentNotSpecified(Export export)
{
EnvironmentSpecificAttribute attribute = export.Value.GetType().GetCustomAttribute<EnvironmentSpecificAttribute>() as EnvironmentSpecificAttribute;
return attribute == null || string.Equals(attribute.TargetEnvironment, _targetEnvironment,
StringComparison.InvariantCultureIgnoreCase);
}
}
最后是上面classes的用法:
static void Main(string[] args)
{
string environmentName = "QA";
EnvironmentSpecificContainer container = new EnvironmentSpecificContainer(new AssemblyCatalog(Assembly.GetCallingAssembly()), environmentName);
var tmp = container.GetExportedValue<object>("MefTest.Consumer");
}