如何在MEF容器中交换/替换共享(单例)对象?

这可能很简单,但由于我是MEF领域的新手,这就是为什么我难以找到问题的解决方案。

我正在使用WPF + Prism和MEF作为DI容器的应用程序。 我想将我的对象(即RuleFile )与每个应用程序实例RuleFile ,方法是将它与文件说法RuleFile1.ruleapp相关联。 因此,我使用属性[PartCreationPolicy(CreationPolicy.Shared)]进行了[PartCreationPolicy(CreationPolicy.Shared)] ,将其视为单例,以便它在整个应用程序中与每个应用程序实例保持相同。

 [Serializable()] [Export] [PartCreationPolicy(CreationPolicy.Shared)] public class RuleFile : NotifyPropertyChanged, IRuleFile { } 

接下来,在ViewModel [ImportingContructor] ,如下所示,该对象与所需的相同。

 [ImportingConstructor] public RuleViewModel(RuleFile ruleFile) [ImportingConstructor] public SchemaViewModel(RuleFile ruleFile) 

到现在为止一切顺利。

使用下面的代码,我试图得到传递给视图模型的相同导出对象,如上所述,但container.GetExportedValue()给出一个不同的新对象引用:

 var catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catalog); var exportObj = container.GetExportedValue(); 

问题1:为什么我得到一个不同的引用虽然对象应该与CreationPolicy.Shared的单例对象相同?

问题2:最终,所有的努力都是用MEF DI容器中的反序列化对象来交换/替换RuleFile导出的对象?

您不会替换MEF容器中的实例,这不是它的工作方式,而且它是危险的并且完全无法管理。 (还要记住C#不是C,你可以简单地改变指向对象)。 假设一个类X从容器中获取一个实例并且有一个工厂方法,在该方法中它将该实例传递给另一个类Y.现在突然想要“替换”该实例。 首先,旧实例会发生什么? 配置? 活着? 邮寄给你的祖母? 其次,如果X使用GetExportedValue获取实例,你将如何通知它它已经消失的实例并被其他东西替换? 你不能……第三,假设X使用了Import而不是魔术它会被通知它的实例已被替换。 现在又如何通知Y实例被替换? 你不能,除非你保留Y.等等的列表,我希望这清楚地说明替换容器中的对象并不是一个好主意。

但是你可以做几件事:

  1. 只需确保在导入任何地方之前创建并注入容器中的RuleFile。 这也是最有意义的:我的印象是RuleFile是某种应用程序范围的配置,因此希望在应用程序启动之前完全设置此配置。 重写MefBootstrapper.ConfigureContainer ,反序列化RuleFile实例并使用ComposeExportedValue将其设置为容器中的唯一实例。 如果反序列化失败,您将显示错误对话框并中止您的应用程序,或者提供默认配置并将其注入。

  2. 提供RuleFile的包装器,如果可用,则从反序列化的RuleFile读取,或者提供默认值。 因此,观察到的行为与在容器中替换的RuleFile相同。 但是,这有一个主要的缺点,如果在加载文件之前有使用IRuleFile实例的代码,它会获得与加载文件后不同的值。 这就是为什么第一种方法更好。 例:

     private class DefaultRuleFile: IRulefile { string SomeProperty { get{ return "DefaultValue"; } } } [Export( typeof( IRulefile ) )] [Export( typeof( RuleFileImplementation ) )] [PartCreationPolicy(CreationPolicy.Shared)] public class RuleFileImplementation : IRulefile { private IRuleFile impl; RuleFileImplementation() { impl = new DefaultRuleFile(); } string SomeProperty { get{ return impl.SomeProperty; } } void LoadFromFile( string file ) { impl = SerializationHelper.Deserialize( file ); } } //at some point in the application: container.GetExportedValue().LoadFromFile( "file" )