有人可以解释一下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;
}
}
答案 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
也会将导出添加到容器中。