SatisfyImportsOnce 与 ComposeParts

2024-01-05

有人可以解释一下两者之间的区别吗SatisfyImportsOnce and ComposeParts为什么一个可以工作而另一个则不行?

具体来说,我有一个正在使用 MEF 的 MVC Web 应用程序。下面是一些在我使用时有效的代码(来自该应用程序)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;
    }
}

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,它可以在没有 MEF 的情况下使用,它更易于测试,并且CompositionContainer将管理零件的使用寿命,在本例中CreationPolicy.Shared(这是导出类型的默认设置),使用单例生命周期策略。然后您可以导入一个实例IFormPartCustomator,您导入相同的单例实例。

我还认为称其为Factory可能是错误的,因为工厂旨在创建新实例,而您的类型只会创建每个实例的一个实例ICustomRenderer。如果这是预期的行为,也许最好将其称为FormPartCustomatorService,它实现了IFormPartCusomatorService界面?如果您想每次启动新实例,您可以查看ExportFactory<ICustomRenderer, ICustomRendererMetadata>.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SatisfyImportsOnce 与 ComposeParts 的相关文章

随机推荐