【问题标题】:Castle Windsor: My code won't work after setting component lifestyle to transientCastle Windsor:将组件生活方式设置为瞬态后,我的代码将无法工作
【发布时间】:2014-06-20 16:11:30
【问题描述】:

我有一个简单的 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 是瞬态的。在这里感谢任何帮助

【问题讨论】:

  • 设置为单例是否有效?
  • 是的,但我希望每次都能使用新实例调用它
  • 你得到什么错误?
  • 没有错误。 Debug writer 的输出表明 MVVM PropertyChanged 事件未在瞬态模式下引发。
  • 我认为这与您的依赖关系有关,在您的情况下,某些模型依赖于其他模型。 stackoverflow.com/questions/3776975/…

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


【解决方案1】:

我与温莎玩得越多,我发现的缺点就越多。这个简单的练习仅作为我是否应该将 Windsor 合并到我自己的框架中的概念证明 (POC)。看来我得放弃了。

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

例如,我的第一篇文章没有成功,因为视图实例订阅了 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),它也无法将此视图引用添加到视图模型。您将收到一个运行时错误,指出未设置对象实例:

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();
}

结论是,Windsor 非常适合单身人士,但对于短暂的生活方式,它无法将实例关系中的最轻微的复杂性联系起来;(

【讨论】:

  • 您好,蒂姆,我认为您的结论为时过早。使用 Windsor 可以连接复杂的依赖关系。在上面的代码中,View 将 VM 作为依赖项,因此当您实例化视图时,会创建一个新的 ViewModel(您告诉它是瞬态的,而不是单例的)。在编写良好的应用程序中,您应该只调用一次 container.Resolve。我对您(错误地)回答您自己的问题并判断您显然还不了解的一个很棒的图书馆投了反对票。首先了解依赖注入的概念,然后才会重视 Ioc 容器。
【解决方案2】:

经过一番折腾后,我发现问题出在 Windsor 为您注入所有依赖项。如果该对象由 Windsor 注册和管理,则您不能将自己的对象作为构造函数参数传递。

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

我无法调试它,因为依赖项是由 Windsor 注入的,并且没有在我的代码中引用,所以我无法“观察”它们发生了什么。

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

主要代码如下: 1)创建模型IDeveloper 2) 使用上面的 IDevoper 作为构造函数参数创建视图模型 3) 以上述视图模型作为构造函数参数创建视图

这就是我得到的:

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

使用开发者 Gtk.WindsorNini.DeveloperB 创建的 MVVM

使用开发者 Gtk.WindsorNini.DeveloperA 创建的 MVVM

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

我没有时间和精力去深入研究 Windsor 的源代码以了解幕后情况,但我的猜测是第一个构造函数调用被丢弃了,因为 Windsor 认识到参数 IDeveloper 应该由它注入,因此它打了第二个电话。

【讨论】:

    【解决方案3】:

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

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

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

    顺便说一句,如果您事先做好了功课,您就会知道使用城堡或任何其他此类容器的全部原因是通过让容器解析来减轻对象的创建和管理em> 对象和 inject 丢失的对象本身。这是设计使然,而不是“问题”。 如果您想手动处理此类职责,那么使用 DI/IoC 容器来管理该特定对象将不适合您的逻辑。

    我个人对使用由 castle 解析的瞬态视图模型并在需要时通过我的事件聚合器的单例传递任何要由其他视图模型处理的事件没有任何问题,所有这些都无需直接从每个视图模型中知道我的目标。

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

    【讨论】:

    • 是的,我刚刚从作业中了解到这一点;P 感谢您的建议,并会调查它
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-12
    • 2010-10-09
    • 2011-05-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多