Castle Windsor:在将组件生活方式设置为瞬态后,我的代码无法工作

时间:2014-05-04 08:32:45

标签: c# mvvm castle-windsor ioc-container

我有一个简单的Winform MVVM设置,用于更新开发人员信息,在收到更新通知后,视图将弹出一个包含新开发人员信息的对话框。使用XML配置中的Castle Windsor容器实例化对象。一切正常,直到我将MVVM组件更改为瞬态。我在下面提供了一些代码。希望它们足够了:

class Program
{
  static void Main(string[] args)
  {
    IDeveloper developer = IoC.Container.Resolve<IDeveloper>();
    IMVVM viewModel = IoC.Container.Resolve<IMVVM>(new Arguments(new
      { MyDeveloper = developer }));
    View view = IoC.Container.Resolve<View>(new Arguments(new
      { MyViewModel = viewModel }));
    viewModel.ChangeDeveloperInfo("Mike Wise");
  }
}

public class MVVM : IMVVM, INotifyPropertyChanged
{
    private IDeveloper developer;
    public event PropertyChangedEventHandler PropertyChanged;

    public MVVM(IDeveloper MyDeveloper)
    {
        developer = MyDeveloper;
    }

    protected void RaiseDeveloperInfoChanged(PropertyChangedEventArgs e)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }

    public void ChangeDeveloperInfo(string strNewName)
    {
        developer.Name = strNewName;
        RaiseDeveloperInfoChanged(new PropertyChangedEventArgs("Name"));
    }

    public string GetName()
    {
        return developer.Name;
    }

    public string GetProgramName()
    {
        return developer.ProgramName;
    }

    public string GetWebsite()
    {
        return developer.Website;
    }

    public string GetComments()
    {
        return developer.Comments;
    }
}

public abstract class View
{
    protected IMVVM ViewModel;

    public View(IMVVM MyViewModel)
    {
        this.ViewModel = MyViewModel;
        this.ViewModel.PropertyChanged += new PropertyChangedEventHandler(DeveloperInfoChanged_Handler);
    }

    protected virtual void DeveloperInfoChanged_Handler(object sender, PropertyChangedEventArgs e)
    {
        UpdateDisplay();
    }

    protected abstract void UpdateDisplay();
}

public class WinFormView : View
{
    private NAboutDialog about;

    public WinFormView(IMVVM viewModel) : base(viewModel)
    {
        this.about = new NAboutDialog();
    }

    protected override void UpdateDisplay()
    {
        about.DeveloperName = this.ViewModel.GetName();
        about.ProgramName = this.ViewModel.GetProgramName();
        about.Website = this.ViewModel.GetWebsite();
        about.Comments = this.ViewModel.GetComments();
        about.ShowDialog();
    }
}

<component id="developerC"
  service="Gtk.WindsorNini.IDeveloper, Gtk.WindsorNini"
  type="Gtk.WindsorNini.DeveloperC, Gtk.WindsorNini"
  lifestyle="transient">
</component>

<component id="mvvm"
  service="Gtk.WindsorNini.IMVVM, Gtk.WindsorNini"
  type="Gtk.WindsorNini.MVVM, Gtk.WindsorNini"
  lifestyle="transient">
</component>

<component id="gtk_view"
  service="Gtk.WindsorNini.View, Gtk.WindsorNini"
  type="Gtk.WindsorNini.GtkView, Gtk.WindsorNini"
  lifestyle="transient">
</component>
<component id="winform_view"
  service="Gtk.WindsorNini.View, Gtk.WindsorNini"
  type="Gtk.WindsorNini.WinFormView, Gtk.WindsorNini"
  lifestyle="transient">
</component>

原谅我的长代码。问题实际上是MVVM是暂时的。感谢这里的任何帮助

3 个答案:

答案 0 :(得分:0)

经过一些更多的摆弄我发现问题确实是温莎为你注入所有的依赖。如果该对象由Windsor注册和管理,则无法将自己的对象作为构造函数参数传递。

就我而言,我想拥有自己对viewmodel和视图的引用。但是,在创建视图时,Windsor将创建viewmodel的新实例,而不使用您提供的实例。

我无法对此进行调试,因为依赖项是由Windsor注入的,而不是在我的代码中引用,因此我无法观察&#34;他们在做什么。

我在阅读Marwijn的评论后所做的是将viewmodel输出到其构造函数中的调试编写器,这样我就可以看到每次调用它时都创建了哪个IDeveloper。

主要代码的工作原理如下: 1)创建模型IDeveloper 2)使用上面的IDeveloper作为构造函数参数创建viewmodel 3)使用上面的viewmodel作为构造函数参数

创建视图

这就是我得到的:

  

作为参数传递的开发人员:Gtk.WindsorNini.DeveloperB

     

使用开发人员Gtk.WindsorNini.DeveloperB

创建的MVVM      

使用开发人员Gtk.WindsorNini.DeveloperA

创建的MVVM

请注意,viewmodel MVVM的构造函数被调用两次 - 首先使用我提供的DeveloperB - 其次是Windsor注入的DeveloperA(默认)

我无法花时间和精力深入了解Windsor的源代码,看看幕后发生了什么,但我的猜测是第一个构造函数调用被丢弃,因为Windsor认识到了参数IDeveloper应该由它注入,因此它进行了第二次调用。

答案 1 :(得分:0)

您似乎通过直接向彼此注册事件来尝试链接 2个视图。这会在2个视图之间产生紧密耦合,并导致您刚才遇到的不必要的场景。

我建议你研究一下你的框架中似乎缺少的事件聚合器模式。这将允许您构建松散耦合的视图模型,而不必担心它们之间有直接引用。 作为caliburn.micro框架的用户,您可能会在这里找到一些有用的信息: Caliburn Micro Event Aggregator

您的问题不是源于使用城堡或DI / IoC,而是源于找到适当的机制来同步您的视图模型(视图)。

顺便说一句,如果你事先做过你的作业,你就会知道使用城堡或任何其他类型的容器的全部理由是通过让容器解决来减轻对象的创建和管理对象和注入缺少的对象本身。这是设计而不是“问题”。 如果您想手动处理此类职责,那么使用DI / IoC容器来管理该特定对象将不适合您的逻辑。

我个人没有问题使用由城堡解决的瞬态视图模型,并在需要时通过我的事件聚合器的单个部分管理任何事件由其他视图模型处理,所有这些都无需直接从每个视图模型中了解我的目标。

我希望这可能对您的应用程序设计有所帮助。

答案 2 :(得分:-1)

我和温莎玩的越多,我发现的缺点越多。这个简单的练习只是作为一个概念证明(POC),我是否应该将Windsor纳入我自己的框架。看起来我不得不放弃它。

组件生命周期默认为单例的原因是因为它适用于我测试过的所有情况。当您将其更改为transient时,创建的尝试建立彼此引用的实例将失败。

例如,我的第一篇文章没有用,因为视图实例订阅了viewmodel事件:

public View(IMVVM MyViewModel)
{
  this.ViewModel = MyViewModel;
  //The code below will fail to register the handler but it won't raise any errors
  this.ViewModel.PropertyChanged += new PropertyChangedEventHandler(DeveloperInfoChanged_Handler);
}

protected virtual void DeveloperInfoChanged_Handler(object sender, PropertyChangedEventArgs e)
{
  //This will not be triggered by an update in the viewmodel
  UpdateDisplay();
}

现在,如果我在视图构造函数中添加了一个Visit(this),它也无法将此视图引用添加到viewmodel。您将收到一个运行时错误,指出未设置对象实例:

public View(IMVVM MyViewModel)
{
  this.ViewModel = MyViewModel;
  //Below makes viewmodel reference this view instance
  this.ViewModel.Visit(this);
  this.ViewModel.PropertyChanged += new PropertyChangedEventHandler(DeveloperInfoChanged_Handler);
}

//Viewmodel updates the view when model data changes
public void ChangeDeveloperInfo(string strNewName)
{
  developer.Name = strNewName;
  view.UpdateDisplay();
}

结论是,温莎在单身人士方面表现出色,但在短暂的生活方式下,它无法在实例关系中加入最轻微的并发症;(