能否对MEF导入进行参数化?

4

我相对于MEF来说还是新手,因此我不完全了解其功能。我正在尝试实现类似于Unity的InjectionMember的功能。

假设我有一个导入MEF部件的类。为简单起见,我们以以下类作为导出部分的示例。

[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class Logger {

    public string Category {
        get;
        set;
    }

    public void Write(string text) {
    }

}

public class MyViewModel {

    [Import]
    public Logger Log {
        get;
        set;
    }

}

现在我想弄清楚的是,在导入时是否可以指定Category属性的值。类似于:

public class MyViewModel {

    [MyImportAttribute(Category="MyCategory")]
    public Logger Log {
        get;
        set;
    }

}

public class MyOtherViewModel {

    [MyImportAttribute(Category="MyOtherCategory")]
    public Logger Log {
        get;
        set;
    }

}

目前,我正在实现IPartImportsSatisfiedNotification并在代码中设置类别。但显然,我更愿意将所有内容整齐地放在一个地方。

2个回答

6
MEF编程指南中,阅读导出和元数据部分。它展示了如何通过使用ExportMetadata属性或定义自己的自定义导出属性,在导出部分上添加元数据。
然后,您可以像这样定义一个ILoggerMetadata接口:
public interface ILoggerMetadata
{
    string Catagory { get; }
}

然后执行 ImportMany 操作,导入一个 IEnumerable<Lazy<ILogger,ILoggerMetadata>>,在代码中选择需要的一个,如下所示:

private ILogger fooLogger;

[ImportMany]
public IEnumerable<Lazy<ILogger,ILoggerMetadata>> Loggers
{
    set
    {
        this.fooLogger = value.First(x => x.Metadata.Catagory == "foo").Value;
    }
}

我同意将元数据约束直接放在导入属性中会更好,但是目前MEF不能直接实现这一点。(可能可以扩展MEF来实现此功能。)
另一种方法是从ILogger派生出一个IFooLogger接口,并在导入和导出中使用它。这很简单,基本上与在导入中放置约束具有相同的效果。然而,如果您有多个元数据属性和/或许多可能的值,则此方法无法工作。
编辑:我稍微误解了您的问题;我以为它是关于限制导入而不是使用一些额外参数配置导入对象。
我认为Kathleen Dollard的最近的这篇文章也是关于同样的问题。此外,在有关组件关系的这篇文章中,Nicholas Blumhardt将这种“参数化”关系建模为Func<X,Y>(或在您的情况下为Func<ILogger,string>的注入)。
您可以通过直接在方法上放置[Export(typeof(Func<ILogger,string>))]属性来在MEF中执行相同操作。或者如果您需要一个不那么模糊的契约,您可以定义一个ILoggerFactory接口并进行导入/导出:
public ILoggerFactory
{
    ILogger Create(string category);
}

最终,你仍然需要在代码中调用工厂。

但是如果我理解正确的话,那就需要在导出时指定有哪些类别。基本上,我想让导入者带入他们自己(非共享)的 ILogger 实例,并声明性地指定其类别。但我认为 MEF 目前没有扩展点让我用属性模型来实现这一点。无论如何,似乎我都必须在代码中完成它。 - Josh
尽管发布后我意识到,虽然我仍然必须在代码中完成它,但如果我使用普通的字段支持属性,我可以在setter中设置类别。这至少比在IPartImportsSatisfiedNotification中完成要好。 - Josh
嗯,你能把工厂推荐单独分成一个答案吗?我想点赞那个,但不想点赞其他的东西;-)。 - binki

3

在进一步研究MEF后,似乎没有声明性地完成这个任务的方法。虽然您可以派生自己的导出属性,但似乎没有任何机制以有意义的方式派生导入属性。

但是,与其实现IPartImportsSatisfiedNotification接口,我可以在setter中设置类别(现在看来很明显)。我必须放弃自动属性,但这就是生活。

public class MyViewModel {

    private Logger log;

    [Import]
    public Logger Log {
        get { return log; }
        set {
            log = value;
            log.Category = "MyCategory";
        }
    }

}

是的,这大概就是你能够得到的最接近你所想要的了。 - Wes Haggard
或者使用工厂模式,其中工厂由MEF实例化,但工厂本身管理实例化的实例。这样,如果您想避免这些属性,可以避免具有公开可写属性。 - binki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接