如何使用 MEF 中的元数据创建合成范围?
How can I create composition scoping by using metadata in MEF?
我的目标是为应用程序的某些部分共享对象实例。一个容器内有许多不同类型的模块,因此必须有动态的方式来定义哪些实例应该在哪个范围内共享。
当我使用过滤目录、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);
问题
我发现我只能在已导出的具体 class 中使用 PartMetadataAttribute。有什么方法可以从基础/抽象中添加 PartMetadata class?
如果没有办法做到这一点 - 还有其他方法可以进行这种动态范围界定吗?
[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" 导出。任何导入它(传递地)的人都自动属于 App 范围(因为他们必须是才能导入它)。任何未被该传递性导入步骤选中的内容都会自动冒泡到全局范围。我实际上是用 4 个作用域完成的:最低、中等、较高作用域,加上一个全局作用域,如果它们不导入特殊的 scope-defining 导出,其他所有内容都会冒泡。在每个范围中,都有一个特殊的 MEF 部分定义该范围的锚点。这部分是 T
in 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;
}
希望对您有所帮助。我认为它会的,因为它会让你到达听起来像你要去的地方,这是隐式范围的部分,因为它们 import/export.
也就是说,您可以考虑使用 NuGet 中的 Microsoft.Composition,它具有更完整的范围界定功能。但与 .NET 中的 MEF 相比,它也有很多限制。所以它可能适合也可能不适合。
大多数模块只从核心库中导入东西,但也有在不同模块之间共享的程序集。我已经尝试阅读所有与 MEF 在 visual studio 中如何使用相关的文章和博客文章,并且还对 Roslyn System.Reflection.Metadata - https://github.com/KirillOsenkov/MEFMetadata/
进行了一些测试
我的目标是为应用程序的某些部分共享对象实例。一个容器内有许多不同类型的模块,因此必须有动态的方式来定义哪些实例应该在哪个范围内共享。
当我使用过滤目录、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);
问题
我发现我只能在已导出的具体 class 中使用 PartMetadataAttribute。有什么方法可以从基础/抽象中添加 PartMetadata class?
如果没有办法做到这一点 - 还有其他方法可以进行这种动态范围界定吗?
[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" 导出。任何导入它(传递地)的人都自动属于 App 范围(因为他们必须是才能导入它)。任何未被该传递性导入步骤选中的内容都会自动冒泡到全局范围。我实际上是用 4 个作用域完成的:最低、中等、较高作用域,加上一个全局作用域,如果它们不导入特殊的 scope-defining 导出,其他所有内容都会冒泡。在每个范围中,都有一个特殊的 MEF 部分定义该范围的锚点。这部分是 T
in 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;
}
希望对您有所帮助。我认为它会的,因为它会让你到达听起来像你要去的地方,这是隐式范围的部分,因为它们 import/export.
也就是说,您可以考虑使用 NuGet 中的 Microsoft.Composition,它具有更完整的范围界定功能。但与 .NET 中的 MEF 相比,它也有很多限制。所以它可能适合也可能不适合。
大多数模块只从核心库中导入东西,但也有在不同模块之间共享的程序集。我已经尝试阅读所有与 MEF 在 visual studio 中如何使用相关的文章和博客文章,并且还对 Roslyn System.Reflection.Metadata - https://github.com/KirillOsenkov/MEFMetadata/
进行了一些测试