繁体   English   中英

如何通过在MEF中使用元数据来创建合成作用域?

[英]How can I create composition scoping by using metadata in MEF?

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

当我使用带有PartMetadata属性的Filtered目录,CompositionScopeDefinition时,此想法很好用。 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 有什么方法可以从基类/抽象类中添加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
{

}

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

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

过去,对于大型MEF目录,我所做的并不是基于零件的输出,而是零件的输入。 例如,您有一个“应用程序”导出。 (间接地)导入它的任何人都自动属于App范围(因为必须导入才能导入)。 该可传递导入遍历未选择的所有内容都会自动冒泡至全局范围。 实际上,我使用4个作用域进行了此操作:最低,中间,较高作用域,以及一个全局作用域,如果它们不导入定义特殊作用域的导出,则其他所有对象都会冒泡。 在每个范围中,都有一个特殊的MEF部分,用于定义该范围的锚点。 这一部分是父作用域中ExportFactory<T>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相比,它也有很多限制。 因此,它可能是一个合适的选择,也可能不是一个合适的选择。

大多数模块仅从核心库中导入内容,但也有在不同模块之间共享的程序集。 我已经尝试阅读所有有关如何在Visual Studio中使用MEF的文章和博客文章,并且还对Roslyn System.Reflection.Metadata进行了少许测试-https: //github.com/KirillOsenkov/MEFMetadata/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM