如何通过在MEF中使用元数据来创建组合范围?

时间:2016-01-18 09:43:20

标签: c# mef

我的目标是为应用程序的某些部分共享对象的实例。一个容器中有许多不同类型的模块,因此必须有动态方式来定义哪些实例应该在哪个范围内共享。

当我使用Filtered catalogs,CompositionScopeDefinition和PartMetadata属性时,这个想法工作得很好。 PartMetadatas由FilteredCatalogs使用。

var fullCatalog = new AggregateCatalog();

// lots of different kind of modules added inside aggregate catalog here

var globalLevelCatalog = fullCatalog.Filter(cpd => 
    !cpd.ContainsPartMetadataWithKey("Scope.App") || 
    cpd.ContainsPartMetadataWithKey("Scope.Global")
);

var appLevelCatalog = fullCatalog.Filter(cpd => 
    cpd.ContainsPartMetadataWithKey("Scope.App")
);

var appLevelDefinition = new CompositionScopeDefinition(appLevelCatalog, null);

var globalDefinition = new CompositionScopeDefinition(
    globalLevelCatalog, new[] { appLevelDefinition }
);

var container = new CompositionContainer(globalDefinition);

问题

我发现只能在导出的具体类中使用 PartMetadataAttribute 。有没有办法从base / abstract类添加PartMetadata?

如果没有办法做到这一点 - 还有其他方法可以做这种动态范围吗?

[PartMetadata("Scope.App", true)]  // will not be part of Metadata
public abstract class AppBase : IApp
{

}

// The PartMetadata has to be defined here so it is part of Metadata
[Export(typeof(IApp))]
public class TheApp
{

}

2 个答案:

答案 0 :(得分:2)

可以继承导出和导出元数据。但我认为部分元数据不能被继承。

您所描述的内容似乎不是我所谓的动态范围,因为事实上范围是固定的。只是你希望它根据导出的内容自动传播到不同的部分。是吗?

我过去为一个非常大的MEF目录所做的不是基于哪些部分导出,而是基于它们导入的内容。例如,您有一个“App”导出。任何导入它(传递)的人都自动属于App范围(因为它们必须是为了导入它)。任何未通过该传递导入步骤选择的内容都会自动冒泡到全局范围。我实际上用4个范围做了这个:最低,中间,高范围,加上一个全局范围,如果它们不导入特殊范围定义导出,其他所有内容都会冒泡。在每个范围中,都有一个特殊的MEF部分,用于定义该范围的锚点。此部分是来自父作用域的T中的ExportFactory<T>,它创建较低作用域的实例。我使用MEF已经在FilteredCatalog上定义的扩展方法来进行传递导入步骤,它实际上是非常简单的代码。

下面的方法将所有部分的单个目录拆分为范围目录。它支持 n 范围深度。您可以使用结果来构建CompositionScopeDefinition

private static IImmutableDictionary<string, ComposablePartCatalog> GetScopedCatalogs(ComposablePartCatalog fullCatalog)
{
    Requires.NotNull(fullCatalog, "fullCatalog");

    // define the scopes, in order of lowest scope to highest.
    // The implicit one is the "" scope, which is global.
    string[] scopes = { typeof(DeepestScopePart).FullName, typeof(MiddleScopePart).FullName, typeof(UpperScopePart).FullName };

    var scopesAndCatalogs = ImmutableDictionary.Create<string, ComposablePartCatalog>().WithComparers(StringComparer.Ordinal);
    var catalog = fullCatalog; // start with the full catalog
    foreach (string scope in scopes)
    {
        // Pull out this scoped contract and everything that depends on it.
        var scopedCatalog = catalog
            .Filter(part => part.Exports(scope))
            .IncludeDependents(def => def.Cardinality != ImportCardinality.ZeroOrMore);
        scopesAndCatalogs = scopesAndCatalogs.Add(scope, scopedCatalog);

        // What's left over goes to the next scope up.
        catalog = scopedCatalog.Complement;
    }

    // define the implicit one (global catches every part that doesn't need a lower scope).
    scopesAndCatalogs = scopesAndCatalogs.Add(String.Empty, catalog);

    return scopesAndCatalogs;
}

我希望有所帮助。我认为这样做会因为它会让你听到你的声音,这是因为它们导入/导出的内容而隐含的部分。

也就是说,您可以考虑使用NuGet中的Microsoft.Composition,它具有更完整的范围界定功能。但与.NET中的MEF相比,它也有很多限制。所以它可能适合也可能不适合。

答案 1 :(得分:0)

大多数模块只从核心库中导入东西,但也有不同模块之间共享的程序集。我试图阅读有关如何在视觉工作室内使用MEF的所有文章和博客文章,以及一点点测试Roslyn System.Reflection.Metadata - https://github.com/KirillOsenkov/MEFMetadata/