0

这可能很简单,但由于我是 MEF 竞技场的新手,所以我很难找出解决问题的方法。

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

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

接下来,在[ImportingContructor]如下所示的 ViewModel 时,对象与想要的一样。

[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:最终这一切的努力是RuleFile用 MEF DI 容器中的反序列化对象交换/替换导出的对象?

4

1 回答 1

1

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

不过,您可以做几件事:

  1. 只需确保在将 RuleFile 导入到任何地方之前,在容器中创建并注入您的 RuleFile。这也是最有意义的:我的印象是 RuleFile 是某种应用程序范围的配置,因此最好在应用程序启动之前完全设置此配置。Override 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" )
    
于 2014-09-01T09:20:44.400 回答