SatisfyImportsOnce vs ComposeParts

Pet*_*ter 15 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Mat*_*ott 22

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

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

[ImportMany(typeof(ICustomRenderer), AllowRecomposition = true)]
Run Code Online (Sandbox Code Playgroud)

...但通过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);
}
Run Code Online (Sandbox Code Playgroud)

我建议构造函数注入的原因是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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

我还认为调用它Factory可能是错误的,因为工厂设计用于创建新实例,而您的类型只会创建每个实例的一个实例ICustomRenderer.如果这是预期的行为,可能会更好地称为FormPartCustomatorService实现IFormPartCusomatorService接口的?如果你想每次都启动新的实例,你可以看一下ExportFactory<ICustomRenderer, ICustomRendererMetadata>.


Dan*_*ted 10

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

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

  • "ComposeParts也会将出口添加到容器中." - 这就是我想要的:) (3认同)