正如these questions中所述,我正在尝试构建一个由主机和多个任务处理客户端组成的应用程序。在一些帮助下,我已经找到了如何发现和序列化部件定义,以便我可以存储这些定义而无需加载实际的运行时类型。
我想要实现的下一步(或接下来的两个步骤)是我想要从实际创建和连接对象(由这些部分表示)中分割部件的组成。所以,如果我有一组部件,那么我希望能够执行以下操作(伪代码):
public sealed class Host
{
public CreationScript Compose()
{
CreationScript result;
var container = new DelayLoadCompositionContainer(
s => result = s);
container.Compose();
return script;
}
public static void Main()
{
var script = Compose();
// Send the script to the client application
SendToClient(script);
}
}
// Lives inside other application
public sealed class Client
{
public void Load(CreationScript script)
{
var container = new ScriptLoader(script);
container.Load();
}
public static void Main(string scriptText)
{
var script = new CreationScript(scriptText);
Load(script);
}
}
这样我可以在宿主应用程序中编写部分,但实际上加载代码并在客户端应用程序中执行它。目标是让所有智能决定在一个位置(主机)加载什么,而实际工作可以在任何地方(由客户端)完成。
基本上我正在寻找的是获取MEF隐式创建的ComposablePart图的一些方法。
现在我的问题是MEF中是否有任何位可以让我实现这种行为?我怀疑provider model可能对我有所帮助,但这是MEF的一个相当大而复杂的部分,所以任何指导都会有所帮助。
答案 0 :(得分:2)
从大量的调查来看,似乎无法将组合过程与MEF中的实例化过程分开,因此我不得不为此问题创建自己的方法。该解决方案假定scanning of plugins导致以某种方式存储类型,导入和导出数据。
为了组成零件,您需要跟踪每个零件实例以及它与其他零件实例的连接方式。最简单的方法是使用图形数据结构来跟踪哪个导入连接到哪个导出。
public sealed class CompositionCollection
{
private readonly Dictionary<PartId, PartDefinition> m_Parts;
private readonly Graph<PartId, PartEdge> m_PartConnections;
public PartId Add(PartDefinition definition)
{
var id = new PartId();
m_Parts.Add(id, definition);
m_PartConnections.AddVertex(id);
return id;
}
public void Connect(
PartId importingPart,
MyImportDefinition import,
PartId exportingPart,
MyExportDefinition export)
{
// Assume that edges point from the export to the import
m_PartConnections.AddEdge(
new PartEdge(
exportingPart,
export,
importingPart,
import));
}
}
请注意,在连接两个部件之前,有必要检查导入是否可以连接到导出。在其他情况下,MEF会这样做,但在这种情况下,我们需要自己做。如何处理的一个例子是:
public bool Accepts(
MyImportDefinition importDefinition,
MyExportDefinition exportDefinition)
{
if (!string.Equals(
importDefinition.ContractName,
exportDefinition.ContractName,
StringComparison.OrdinalIgnoreCase))
{
return false;
}
// Determine what the actual type is we're importing. MEF provides us with
// that information through the RequiredTypeIdentity property. We'll
// get the type identity first (e.g. System.String)
var importRequiredType = importDefinition.RequiredTypeIdentity;
// Once we have the type identity we need to get the type information
// (still in serialized format of course)
var importRequiredTypeDef =
m_Repository.TypeByIdentity(importRequiredType);
// Now find the type we're exporting
var exportType = ExportedType(exportDefinition);
if (AvailableTypeMatchesRequiredType(importRequiredType, exportType))
{
return true;
}
// The import and export can't directly be mapped so maybe the import is a
// special case. Try those
Func<TypeIdentity, TypeDefinition> toDefinition =
t => m_Repository.TypeByIdentity(t);
if (ImportIsCollection(importRequiredTypeDef, toDefinition)
&& ExportMatchesCollectionImport(
importRequiredType,
exportType,
toDefinition))
{
return true;
}
if (ImportIsLazy(importRequiredTypeDef, toDefinition)
&& ExportMatchesLazyImport(importRequiredType, exportType))
{
return true;
}
if (ImportIsFunc(importRequiredTypeDef, toDefinition)
&& ExportMatchesFuncImport(
importRequiredType,
exportType,
exportDefinition))
{
return true;
}
if (ImportIsAction(importRequiredTypeDef, toDefinition)
&& ExportMatchesActionImport(importRequiredType, exportDefinition))
{
return true;
}
return false;
}
请注意,特殊情况(如IEnumerable<T>
,Lazy<T>
等)需要确定导入类型是否基于可能有点棘手的泛型类型。
一旦存储了所有组成信息,就可以在任何时间点对部件进行实例化,因为所有必需的信息都是可用的。实例化需要慷慨的反思和可信赖的Activator课程的使用,并留给读者作为练习。