SatisfyImportsOnce vs ComposeParts

时间:2011-06-21 01:12:49

标签: c# mef

有人可以解释一下SatisfyImportsOnceComposeParts之间的区别,以及为什么一个人会在另一个没有的情况下工作?

具体来说,我有一个MVC Web应用程序,我正在使用MEF。下面是一些代码(来自该应用程序),当我使用SatisfyImportsOnce但是在我使用ComposeParts时不起作用。我的理解是ComposeParts从属性对象数组创建可组合部分,并在指定的组合容器中组合它们,SatisfyImportsOnce通过使用指定的组合服务组成指定的部分。对于我简单的猴子脑,即使英语不同,它们在语义上是相同的。两者都使用CompositionContainer在导入目标上吐出导出的类型。

public class FormPartCustomatorFactory
{

    [ImportMany(typeof(ICustomRenderer), AllowRecomposition = true)]
    private readonly List<Lazy<ICustomRenderer, ICustomRendererMetaData>> _rendererImports = new List<Lazy<ICustomRenderer, ICustomRendererMetaData>>();

    private readonly Dictionary<string, Lazy<ICustomRenderer, ICustomRendererMetaData>> _renderers;

    public static readonly FormPartCustomatorFactory Instance = new FormPartCustomatorFactory();

    static CompositionContainer _container;

    private FormPartCustomatorFactory()
    {
        using (var catalog = new DirectoryCatalog(HttpRuntime.BinDirectory, "*.dll"))
        {               
            _container = new CompositionContainer(catalog);
            _container.SatisfyImportsOnce(this); // <- Works
            // _container.ComposeParts(this); // DOESN'T Work
            _renderers = _rendererImports.ToDictionary(q => q.Metadata.Name, q => q);
        }
    }

    ~FormPartCustomatorFactory()
    {
        _container.Dispose();
    }

    public static ICustomRenderer Find(string name)
    {
        return Instance._renderers[name].Value;
    }
}

2 个答案:

答案 0 :(得分:22)

SatisyImportsOnce将编写一个类型而不将其注册为重构。因此,如果您打算使用不支持重构的类型,您可以使用SatisfyImportsOnce并且它将照常执行工作,但是容器中的任何更改(添加了新部件或删除了部件),然后是您的实例不会自动重新组合以提供这些新零件。

在您的实例中,您正在使用:

[ImportMany(typeof(ICustomRenderer), AllowRecomposition = true)]

...但是通过SatisfyImportsOnce,不会重新导入此导入。

如果你不担心重构,你可以改变你的代码使用构造函数注入,所以你可以这样做:

[ImportingConstructor]
public FormPartCustomatorFactory(IEnumerable<Lazy<ICustomRenderer, ICustomRendererMetadata>> renderers)
{
    if (renderers == null)
        throw new ArgumentNullException("renderers");

    _renderers = renderers.ToDictionary(r => r.Metadata.Name, r => r);
}

我建议构造函数注入的原因是Lazy<ICustomRenderer, ICustomRendererMetadata>实例集是您的类型所需的显式依赖项,因此最好将类型实例化为可用状态,而不是实例化然后需要为第一次使用做好准备的另一步。

这使您的FormPartCustomatorFactory类型更易于测试。为此,如果您要更改构造函数,那么使其成为单例的方法将无效。相反,您可以利用MEF的生命周期管理功能,因此可能将您的类型重新设计为:

public interface IFormPartCustomatorFactory
{
    ICustomRenderer Find(string name);
}

[Export(typeof(IFormPartCustomerFactory)), PartCreationPolicy(CreationPolicy.Shared)]
public class FormPartCustomatorFactory : IFormPartCustomatorFactory
{
    private IEnumerable<Lazy<ICustomRenderer, ICustomRendereMetadata>> _renderers;

    [ImportingConstructor]
    public FormPartCustomatorFactory(IEnumerable<Lazy<ICustomRenderer, ICustomRendererMetadata>> renderers)
    {
        if (renderers == null)
            throw new ArgumentNullException("renderers");

        _renderers = renderers;
    }

    public ICustomRenderer Find(string name)
    {
        return _renderers
            .Where(r => r.Metadata.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)
            .Select(r => r.Value)
            .FirstOrDefault();
    }
}

这样做意味着你的类型不依赖于MEF,它可以在没有它的情况下使用,它更可测试,CompositionContainer将管理部件的生命周期,在这种情况下{{1 (导出类型的默认值),使用单例生存期策略。然后,您可以导入CreationPolicy.Shared的实例,导入相同的单例实例。

我还认为将其称为IFormPartCustomator可能是错误的,因为工厂旨在创建新实例,而您的类型只会创建每个Factory的一个实例。如果这是预期的行为,那么可能更好地称为ICustomRenderer,它实现了FormPartCustomatorService接口?如果您想每次都启动新实例,可以查看IFormPartCusomatorService

答案 1 :(得分:10)

正如Matthew所提到的,SatisfyImportsOnce没有注册重组的部分。这意味着MEF容器不包含对零件的引用。

此外,SatisfyImportsOnce仅满足部件的导入,但忽略其具有的任何导出。 ComposeParts也会将导出添加到容器中。