如何交换/替换MEF容器中的共享(singleton)对象

本文关键字:共享 对象 singleton 替换 何交换 交换 MEF | 更新日期: 2023-09-27 18:20:11

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

我正在开发一个应用程序,使用WPF+Prism,MEF作为DI容器。我想通过将我的对象(即RuleFile)与RuleFile1.ruleapp等文件关联,将其与每个应用程序实例联系起来。因此,我用属性[PartCreationPolicy(CreationPolicy.Shared)]对其进行了修饰,将其视为singleton,以便在整个应用程序中与每个应用程序实例保持一致。

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

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

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

直到现在一切都很顺利。

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

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

问题1:尽管对象应该与CreationPolicy.Shared的单例对象相同,但为什么我得到了不同的引用?

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

如何交换/替换MEF容器中的共享(singleton)对象

您不能替换MEF容器中的实例,这不是它的工作方式,而且它很危险,完全无法管理。(还要记住,C#不是C,在这里您可以简单地更改指向的对象)。假设一个类X从容器中获得一个实例,并有一个工厂方法,在该方法中它将该实例传递给另一个类Y。首先,旧的例子会发生什么?乐意还活着?给你奶奶写信了?第二,如果X使用GetExportedValue获得了实例,您将如何通知它它所拥有的实例现在已经不存在,并被其他实例替换?你不能。。第三,假设X使用了Import,并且通过某种魔法,它得到了它的实例已被替换的通知。它现在如何依次通知Y实例已被替换?你不能,除非你保留一个Y.Etc等的列表。我希望这表明替换容器中的对象不是一个好主意。

不过,你可以做几件事:

  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<IRuleFile>( file );
      }
    }
    //at some point in the application:
    container.GetExportedValue<RuleFileImplementation>().LoadFromFile( "file" )