加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

如何使用MEF管理相互依赖的模块?

发布时间:2020-12-13 20:47:50 所属栏目:百科 来源:网络整理
导读:我发现这个问题难以表达(特别是标题形式),所以请耐心等待. 我有一个应用程序,我不断修改,以做不同的事情.似乎MEF可能是管理不同功能的好方法.从广义上讲,应用程序的三个部分构成了各种管道: 收购 转型 表达 在它最简单的形式中,我可以将每个阶段表达为一个
我发现这个问题难以表达(特别是标题形式),所以请耐心等待.

我有一个应用程序,我不断修改,以做不同的事情.似乎MEF可能是管理不同功能的好方法.从广义上讲,应用程序的三个部分构成了各种管道:

>收购
>转型
>表达

在它最简单的形式中,我可以将每个阶段表达为一个接口(IAcquisition等).当我想使用提供比标准数据更丰富的数据的采集组件时,问题就开始了.我想设计使用这些更丰富数据的模块,但我不能依赖它在那里.

当然,我可以将所有数据添加到接口规范中.我可以通过抛出异常或返回空值来处理较差的数据源.这似乎与理想相去甚远.

我更喜欢在三个阶段中进行MEF绑定,这样只有当模块与之前选择的模块兼容时才会向用户提供模块.

所以我的问题是:我可以指定限制可用导入集的元数据吗?

一个例子:

Acquision1 offers BasicData only

Acquision2 offers BasicData and AdvancedData

Transformation1 requires BasicData

Transformation2 requires BasicData and AdvancedData

Acquisition module is selected first.

If Acquisition1 is selected,don’t offer Transformation 2,otherwise offer both.

这可能吗?如果是这样,怎么样?

你的问题建议像这样的结构:
public class BasicData
{
    public string Basic { get; set; } // example data
}

public class AdvancedData : BasicData
{
    public string Advanced { get; set; } // example data
}

现在您拥有了采集,转换和表达组件.您希望能够处理不同类型的数据,因此它们是通用的:

public interface IAcquisition<out TDataKind>
{
    TDataKind Acquire();
}

public interface ITransformation<TDataKind>
{
    TDataKind Transform(TDataKind data);
}

public interface IExpression<in TDataKind>
{
    void Express(TDataKind data);
}

现在你想要构建一个看起来像这样的管道:

IExpression.Express(ITransformation.Transform(IAcquisition.Acquire));

那么让我们开始构建一个管道构建器:

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Linq;
using System.Linq.Expressions;

// namespace ...

public static class PipelineBuidler
{
    private static readonly string AcquisitionIdentity =
        AttributedModelServices.GetTypeIdentity(typeof(IAcquisition<>));
    private static readonly string TransformationIdentity =
        AttributedModelServices.GetTypeIdentity(typeof(ITransformation<>));
    private static readonly string ExpressionIdentity =
        AttributedModelServices.GetTypeIdentity(typeof(IExpression<>));

    public static Action BuildPipeline(ComposablePartCatalog catalog,Func<IEnumerable<string>,int> acquisitionSelector,int> transformationSelector,int> expressionSelector)
    {
        var container = new CompositionContainer(catalog);

该类为您的三个合约接口保存MEF类型标识.我们稍后需要那些来确定正确的出口.我们的BuildPipeline方法返回一个Action.那将是管道,所以我们可以做管道().它需要一个ComposablePartCatalog和三个Funcs(以选择导出).这样,我们可以将所有肮脏的工作保留在这个类中.然后我们从创建CompositionContainer开始.

现在我们必须构建ImportDefinitions,首先是获取组件:

var aImportDef = new ImportDefinition(def => (def.ContractName == AcquisitionIdentity),null,ImportCardinality.ZeroOrMore,true,false);

此ImportDefinition只过滤掉IAcquisition的所有导出<>接口.现在我们可以把它交给容器了:

var aExports = container.GetExports(aImportDef).ToArray();

aExports现在拥有所有IAcquisition<>目录中的出口.那么让我们运行选择器:

var selectedAExport = aExports[acquisitionSelector(aExports.Select(export => export.Metadata["Name"] as string))];

我们有我们的收购部分:

var acquisition = selectedAExport.Value;
        var acquisitionDataKind = (Type)selectedAExport.Metadata["DataKind"];

现在我们将对转换和表达式组件执行相同的操作,但略有不同:ImportDefinition将确保每个组件都可以处理前一个组件的输出.

var tImportDef = new ImportDefinition(def => (def.ContractName == TransformationIdentity) && ((Type)def.Metadata["DataKind"]).IsAssignableFrom(acquisitionDataKind),false);
        var tExports = container.GetExports(tImportDef).ToArray();
        var selectedTExport = tExports[transformationSelector(tExports.Select(export => export.Metadata["Name"] as string))];

        var transformation = selectedTExport.Value;
        var transformationDataKind = (Type)selectedTExport.Metadata["DataKind"];

        var eImportDef = new ImportDefinition(def => (def.ContractName == ExpressionIdentity) && ((Type)def.Metadata["DataKind"]).IsAssignableFrom(transformationDataKind),false);
        var eExports = container.GetExports(eImportDef).ToArray();
        var selectedEExport = eExports[expressionSelector(eExports.Select(export => export.Metadata["Name"] as string))];

        var expression = selectedEExport.Value;
        var expressionDataKind = (Type)selectedEExport.Metadata["DataKind"];

现在我们可以在表达式树中将它们连接起来:

var acquired = Expression.Call(Expression.Constant(acquisition),typeof(IAcquisition<>).MakeGenericType(acquisitionDataKind).GetMethod("Acquire"));
        var transformed = Expression.Call(Expression.Constant(transformation),typeof(ITransformation<>).MakeGenericType(transformationDataKind).GetMethod("Transform"),acquired);
        var expressed = Expression.Call(Expression.Constant(expression),typeof(IExpression<>).MakeGenericType(expressionDataKind).GetMethod("Express"),transformed);
        return Expression.Lambda<Action>(expressed).Compile();
    }
}

就是这样!一个简单的示例应用程序如下所示:

[Export(typeof(IAcquisition<>))]
[ExportMetadata("DataKind",typeof(BasicData))]
[ExportMetadata("Name","Basic acquisition")]
public class Acquisition1 : IAcquisition<BasicData>
{
    public BasicData Acquire()
    {
        return new BasicData { Basic = "Acquisition1" };
    }
}

[Export(typeof(IAcquisition<>))]
[ExportMetadata("DataKind",typeof(AdvancedData))]
[ExportMetadata("Name","Advanced acquisition")]
public class Acquisition2 : IAcquisition<AdvancedData>
{
    public AdvancedData Acquire()
    {
        return new AdvancedData { Advanced = "Acquisition2A",Basic = "Acquisition2B" };
    }
}

[Export(typeof(ITransformation<>))]
[ExportMetadata("DataKind","Basic transformation")]
public class Transformation1 : ITransformation<BasicData>
{
    public BasicData Transform(BasicData data)
    {
        data.Basic += " - Transformed1";
        return data;
    }
}

[Export(typeof(ITransformation<>))]
[ExportMetadata("DataKind","Advanced transformation")]
public class Transformation2 : ITransformation<AdvancedData>
{
    public AdvancedData Transform(AdvancedData data)
    {
        data.Basic += " - Transformed2";
        data.Advanced += " - Transformed2";
        return data;
    }
}

[Export(typeof(IExpression<>))]
[ExportMetadata("DataKind","Basic expression")]
public class Expression1 : IExpression<BasicData>
{
    public void Express(BasicData data)
    {
        Console.WriteLine("Expression1: {0}",data.Basic);
    }
}

[Export(typeof(IExpression<>))]
[ExportMetadata("DataKind","Advanced expression")]
public class Expression2 : IExpression<AdvancedData>
{
    public void Express(AdvancedData data)
    {
        Console.WriteLine("Expression2: ({0}) - ({1})",data.Basic,data.Advanced);
    }
}


class Program
{
    static void Main(string[] args)
    {
        var pipeline = PipelineBuidler.BuildPipeline(new AssemblyCatalog(typeof(Program).Assembly),StringSelector,StringSelector);
        pipeline();
    }

    static int StringSelector(IEnumerable<string> strings)
    {
        int i = 0;
        foreach (var item in strings)
            Console.WriteLine("[{0}] {1}",i++,item);
        return int.Parse(Console.ReadLine());
    }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读