.net MEF';基类中的ImportMany从所有程序集导入所有导出-如何防止这种情况?

.net MEF';基类中的ImportMany从所有程序集导入所有导出-如何防止这种情况?,.net,dependencies,mef,composition,.net,Dependencies,Mef,Composition,我正在使用MEF从几个程序集组合导出的类型。我使用的是一个基类,它应该ImportMany依赖项,正如派生类中指定的那样。它看起来像这样: 基础组件: public abstract class BaseClass { [ImportMany(typeof(IDependency)] public IEnumerable<IDependency> Dependencies { get; private set; } protected BaseClass()

我正在使用MEF从几个程序集组合导出的类型。我使用的是一个基类,它应该
ImportMany
依赖项,正如派生类中指定的那样。它看起来像这样:

基础组件:

public abstract class BaseClass
{
    [ImportMany(typeof(IDependency)]
    public IEnumerable<IDependency> Dependencies { get; private set; }

    protected BaseClass()
    {
        var catalog = GetCatalog();
        var container = new CompositionContainer(catalog);
        container.ComposeParts(this);
    }

    protected abstract ComposablePartCatalog GetCatalog();
}
[Export(typeof(BaseClass))]
public class MyA : BaseClass
{
    protected override ComposablePartCatalog GetCatalog()
    {
        return new AssemblyCatalog(Assembly.GetExecutingAssembly());
    }
}

[Export(typeof(IDependency)]
public class DependencyA1 : IDependency {}

[Export(typeof(IDependency)]
public class DependencyA2 : IDependency {}
[Export(typeof(BaseClass))]
public class MyB : BaseClass
{
    protected override ComposablePartCatalog GetCatalog()
    {
        return new AssemblyCatalog(Assembly.GetExecutingAssembly());
    }
}

[Export(typeof(IDependency)]
public class DependencyB1 : IDependency {}

[Export(typeof(IDependency)]
public class DependencyB2 : IDependency {}
组件B:

public abstract class BaseClass
{
    [ImportMany(typeof(IDependency)]
    public IEnumerable<IDependency> Dependencies { get; private set; }

    protected BaseClass()
    {
        var catalog = GetCatalog();
        var container = new CompositionContainer(catalog);
        container.ComposeParts(this);
    }

    protected abstract ComposablePartCatalog GetCatalog();
}
[Export(typeof(BaseClass))]
public class MyA : BaseClass
{
    protected override ComposablePartCatalog GetCatalog()
    {
        return new AssemblyCatalog(Assembly.GetExecutingAssembly());
    }
}

[Export(typeof(IDependency)]
public class DependencyA1 : IDependency {}

[Export(typeof(IDependency)]
public class DependencyA2 : IDependency {}
[Export(typeof(BaseClass))]
public class MyB : BaseClass
{
    protected override ComposablePartCatalog GetCatalog()
    {
        return new AssemblyCatalog(Assembly.GetExecutingAssembly());
    }
}

[Export(typeof(IDependency)]
public class DependencyB1 : IDependency {}

[Export(typeof(IDependency)]
public class DependencyB2 : IDependency {}
然后,我在基本部件中组合所有内容:

static void Main(string[] args)
{
    DirectoryCatalog catalog = new DirectoryCatalog(path, "*.dll");
    var container = new CompositionContainer(catalog);
    IEnumerable<BaseClass> values = container.GetExportedValues<BaseClass>();

    // both BaseClass instances now have 4 Dependencies - from both Assemby A and Assembly B!
}
static void Main(字符串[]args)
{
DirectoryCatalog=新的DirectoryCatalog(路径“*.dll”);
var容器=新的合成容器(目录);
IEnumerable values=container.GetExportedValues();
//两个基类实例现在都有4个依赖项-来自Assemby A和Assembly B!
}
我遇到的问题是,当我使用MEF组合
MyA
MyB
时,每个都包含从两个程序集导出的
IDependency
-ie!我只希望
MyA
包含导出
DependencyA1
DependencyA2
,与
MyB
相同


我知道我可能应该为此使用依赖注入容器,但我希望可以使用MEF?

在另一篇文章的鼻子底下做一篇文章是非常讨厌的;)

因此,我将解决手动调用container.GetExportedValues的问题,并在构造函数中自行设置属性,这样您就可以同时摆脱[ImportMany]。 而且它没有被操纵在外部构成上


Ariel

您正在围绕MEF进行一些奇怪的舞蹈,使用这种方法,您实际上是多次编写基类,这将根据最后发生的编写给出不同的结果。当前在Main中编写代码的方式将是最后设置ImportMany的方式,因为这发生在对基类的构造函数调用之后。与Ariel相似,我建议你不要在同一个物体上进行多次合成

如果您必须对MEF执行类似操作,我可以从以下几个方面看出它可能会起作用: 1) 不要在构造函数中执行第二次组合,而是使用和在OnImportsAssetIfied中执行第二次组合,尽管在第二次组合时要注意对该方法的第二次调用。 2) 按照Ariel的建议做,不要使用ImportMany,只需执行GetExportedValues,就可以使用其他程序集级目录设置该字段。请记住,这仍然是一个很大的假设,即每个程序集只有一个派生类,否则仍然会遇到重叠。 3) 您可以将ImportMany移动到派生类中,并为每个派生类型(即IADependency或IBDendency)导入唯一的依赖项类型。 4) 可以使用元数据筛选特定派生类型的导入


我不确定这些是否完美,但如果我选择一个选项,我可能会使用#4的一些变体,并使用元数据在导入之间进行过滤。请参阅显示如何订购导入的内容,但存在用于筛选导入的类似代码示例。

太棒了!我删除了
ImportMany
属性,并将其替换为对
GetExportedValues
的简单调用。这就解决了问题!谢谢你,伙计!只是一些注释-使用构造函数进行合成-我已经看到了很多,这不是一个好的设计思想。建议让应用程序的单个部分处理与组合容器有关的所有事情,这称为CompositionRoot。理想情况下,您的组件应该被设计成与任何可扩展性或IoC容器无关,因此您可以独立地构建和测试它们。为了进一步说明这一点,尽管编撰一个新的实例<代码>组件容器相对便宜,但是目录的创建可能是昂贵的。另一点需要考虑的是,您正在从构造函数中调用一个虚拟方法,这意味着您不能再保证您的<代码> BaseClass < /代码>构造函数将完成,因为它现在依赖于对继承层次结构的回调。如果您的
override
方法试图使用您的子类类型的实例成员,您可能会抛出
NullReferenceException
实例,因为这些实例成员可能还没有初始化。@MatthewAbbott是虚拟调用构造函数,您是对的,这就是这个示例。在我的“真实”应用程序中,我不是从构造函数调用虚方法。@MatthewAbbott关于你的第一点,我同意复合根的典型用法。在本例中,我在某种程度上(ab)使用MEF作为DI容器,让它编写自己的类型,同时仍然公开契约,稍后由主应用程序编写。