【问题标题】:Architecture for stateful MVVM application with self contained client-server code and IoC container具有自包含客户端-服务器代码和 IoC 容器的有状态 MVVM 应用程序架构
【发布时间】:2012-11-24 19:51:07
【问题描述】:

我有一个具有多个屏幕并与 WCF Web 服务对话的应用程序。我将 SimpleInjector 用于我最喜欢的 IoC 容器,并将 Caliburn Micro 用于带有 WPF 的 MMVM。

我有一个单独的 ITransportClient 类实现,它控制与 Web 服务的连接以及发送和接收消息。

应用程序具有以下状态:

  1. 启动和初始化应用程序。
  2. 显示登录表单。
  3. 登录表单使用ITransportClient.Login(username, password) 连接到聊天服务器。
  4. 如果登录成功,则显示新的 MainWindow 视图,如果登录失败,则再次提示用户输入凭据。
  5. 登录后,MainWindow 控制发送和接收到 WCF 服务的消息。
  6. 当与服务器断开连接时(由ITransportClientCallback.OnTransportDisconnected 通知),或者当用户单击注销时,再次显示登录表单。

利用我现有的技术,这是我的第一个 MVVM 项目,我有以下架构和设计问题:

  1. 我应该使用什么设计模式来控制显示单独的登录屏幕,然后在连接时显示主屏幕,并在连接丢失时恢复到登录屏幕?
  2. 谁应该控制聊天应用程序的连接状态,这段代码应该放在哪里?这种状态会属于引导程序类吗?
  3. 我是否可以将 ITransportClient 注册为持久性实例范围,并在需要时通过注入需要它的 ViewModel(即登录表单和主窗口表单)来控制多个表单?

ITransportClient接口如下:

public interface ITransportClientCallback
{
    string OnReceivedMessage();
    void OnTransportDisconnected();
}

// transport interface that client calls
public interface ITransportClient
{
    bool SendLogin(string username, string password);
    void SendLogout();
    void SendMessage();
    void RegisterCallback(ITransportClientCallback applicationHostCallback);
}

连同使用 SimpleInjector 的 Caliburn.Micro 引导程序代码:

public class SimpleInjectorBootstrapper : Caliburn.Micro.Bootstrapper
{
    private Container container;

    protected override void Configure()
    {
        this.container = new Container();
        this.container.Register<IWindowManager, WindowManager>();
        this.container.Register<IEventAggregator, EventAggregator>();
        this.container.Register<IAppViewModel, AppViewModel>();
        this.container.RegisterSingle<ITransportClient, Transport.WCF.TransportClient>();
    }

    protected override object GetInstance(Type serviceType, string key)
    {
        return this.container.GetInstance(serviceType);
    }

    protected override IEnumerable<object> GetAllInstances(Type serviceType)
    {
        return this.container.GetAllInstances(serviceType);
    }

    protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
    {
        base.OnStartup(sender, e);
        var appViewModel = this.container.GetInstance<IAppViewModel>();
        var windowManager = this.container.GetInstance<IWindowManager>();
        windowManager.ShowWindow(appViewModel);
    }
}

【问题讨论】:

    标签: mvvm architecture caliburn.micro simple-injector


    【解决方案1】:

    我应该使用什么设计模式来控制显示单独的登录 屏幕,然后在连接时主屏幕,并恢复到 连接丢失时的登录屏幕?

    在很多情况下,MVVM 项目都伴随着“中介”或“信使”。所有的 View Model 都可以通过这种机制,通过订阅和发布消息的方式相互交互。所有虚拟机都会订阅“UserLoggedOutMessage”并做出必要的响应(即将其视觉状态更改为只读,或者根本不显示任何内容)。单独的引导程序还可以将用户“重定向”到登录屏幕。

    谁应该控制聊天应用程序的连接状态以及 这段代码会放在哪里?这个状态会坐在引导程序中吗 上课?

    一种方法是对应用程序的状态进行抽象 - 这等效于 asp.net 中的 HTTPContext.Current 变量。你可以有一个 Session 对象,它有一个 CurrentState 只读属性,对应的 Login()、Logout()、SendMessage() 方法充当状态机。一旦应用程序的状态发生变化,所有视图模型都会订阅事件 - 再次通过调解器。 Session 对象可以是引导程序上的静态变量,也可以是通过 IoC 注入到所有 VM 的单例。

    我可以将 ITransportClient 注册为持久性实例吗 范围,并在需要时通过注入进行多种形式控制 到需要它的 ViewModel(即登录表单和 主窗口窗体)?

    绝对是这个想法。在大多数 MVVM 项目中,您可能希望将依赖项的单个实例注入到 VM 中。这允许更有效的内存使用,还允许一个干净的面向对象模型(与过程不可变事务脚本相反)。

    话虽如此,如果这是尝试检查“应用程序状态”,我会将抽象级别更改为更高的级别,例如IApplicationStateMachine 或 IUserSession。这样,如果您想在一个应用程序实例中支持多个用户,理论上您可以拥有多个 IUserSession 对象实例。

    【讨论】:

    • 感谢 Ronald,我们可以通过从 ViewModel 发送到处理程序的命令进一步抽象出职责 - 允许由每个命令请求管理事务命令和生命周期范围。我已经阅读了 cuttingedge.it/blogs/steven/pivot/entry.php?id=91 并且觉得这很适合这里,如果我们想添加事务支持,我们可以在不修改 ViewModel 的情况下使用命令和将命令包装在适配器中。如果我们直接在 ViewModel 中执行操作,那将违反 Open Closed 原则。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多