MEF:在课程建设之前有条件地选择出口的任何方式吗?

时间:2018-04-20 17:03:43

标签: c# mef

我有两个ISchedulerProvider接口的实现,一个用于测试目的,一个用于非测试执行(Lee Campbell的Intro to Rx的赞美)。我想通过MEF导出两个实例并处理导入,如果我检测到我在测试环境中,请使用测试调度程序,如果我不是,则使用另一个。

我可以在每次导出时设置元数据,在我使用带有相应过滤器的[ImportMany]的任何地方使用ISchedulerProvider,但这似乎是很多开销。有没有办法在课堂建设之前有条件地选择出口?

一个简单的例子非常有用,特别是如果解决方案涉及类似于导出提供程序的东西。

1 个答案:

答案 0 :(得分:1)

我能够通过创建一个继承自CompositionContainer的类来完成此任务,该类根据自定义属性过滤掉导出。

这是一个我被抛在一起的例子:

实施的界面

public interface ISchedulerProvider
{
    string Foo { get; }
}

用于保存目标环境信息的自定义属性,我在此示例中使用了字符串,但我可能在生产代码中使用了Enum:

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

要使用接口的类:

[Export]
public class Consumer
{
    private readonly ISchedulerProvider _schedulerProvider;

    [ImportingConstructor]
    public Consumer(ISchedulerProvider schedulerProvider)
    {
        _schedulerProvider = schedulerProvider;
    }

    public string GetFoo()
    {
        return _schedulerProvider.Foo;
    }
}

自定义CompositionContainer:

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

最后,使用上面的类:

static void Main(string[] args)
{
    string environmentName = "QA";
    EnvironmentSpecificContainer container = new EnvironmentSpecificContainer(new AssemblyCatalog(Assembly.GetCallingAssembly()), environmentName);

    var tmp = container.GetExportedValue<object>("MefTest.Consumer");
}