SatisfyImportsOnce vs ComposeParts
本文关键字:ComposeParts vs SatisfyImportsOnce | 更新日期: 2023-09-27 17:59:51
有人能解释一下SatisfyImportsOnce
和ComposeParts
之间的区别吗?
具体来说,我有一个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;
}
}
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
的一个实例,也就是导入同一个singleton实例。
我还认为,称其为Factory
可能是错误的,因为工厂是为创建新实例而设计的,而您的类型只会为每个ICustomRenderer
创建一个实例。如果这是预期的行为,也许它最好被称为FormPartCustomatorService
,实现IFormPartCusomatorService
接口?如果您想每次都旋转新实例,可以查看ExportFactory<ICustomRenderer, ICustomRendererMetadata>
。
正如Matthew所提到的,SatisfyImportsOnce
不会注册零件进行重组。这意味着MEF容器不包含对零件的引用。
此外,SatisfyImportsOnce
仅满足零件的导入,但忽略其具有的任何导出。ComposeParts
也会将导出添加到容器中。