C# MVVM 处理 UserID 并将其传递给不同的模型和 ViewModel

C# MVVM Handling and Passing UserID to different Models and ViewModels

我目前在我的应用程序中传递 UserID 的实现是通过构造函数。

SomeObject s = new SomeObject(userID)

其中有一个基于用户 ID 执行操作的代码。通过添加另一个名为 "CurrentUser" 的 属性 来进一步跟踪用户 ID,但这似乎是一个肮脏的解决方案,因为我必须将它实现到所有 ViewModels 并且它似乎违反了 "DRY" 概念.

我想到的第二种方法是在我的 MainWindowViewModel 上创建一个 public 静态变量,我的所有其他模型都可以将其引用为 MainWindowViewModel.CurrentUser

这两种方法中的一种是正确的方法还是有我不知道的更好的方法?

不要将当前用户绑定到 ViewModel。我通常选择某种 SessionService。如果您使用依赖注入 (DI),请注册 ISessionService 的单例和具体实现。如果您不使用 DI,那么只需让您的应用程序开始创建一个单例,例如 SessionService.Current。然后你可以把你需要的任何东西放在这里。然后每个 ViewModel 都可以请求 SessionService.Current.User 并且他们拥有它。您的 ViewModel 不应该相互了解,但它们可以了解服务。这使它保持 DRY 和松散耦合,特别是如果您仅使用 ISessionService 的接口而不是具体实现来访问这些会话变量。这使您可以非常轻松地模拟一个,而无需更改任何 ViewModel 代码。

您需要预先仔细分析您希望通过应用程序实现的目标。您对只有一个选定的客户感到满意吗?或者您是否需要同时查看或编辑多个客户端(即您有一个 MDI 风格的应用程序)?

使用单一客户端方法很简单,您可以实现全局 属性 包,如其他答案中已经提到的那样。但我会建议谨慎:如果您在假设只会有一个选定的客户端的基础上构建您的应用程序,那么它就会成为一个真正的 PITA 尝试重构以使其具有多客户端能力。使用像这样的集中式 属性 包或 "session service" 确实是从 VM 中解耦状态,但随着时间的推移,集中式服务仍然会变成一个怪物,并且你对它建立了太多的依赖。

如果您确实想走多客户端路线,那么您就在正确的轨道上 - 但不是在构造函数中传递客户端标识符,而是传递(注入)整个客户端数据对象。很可能您已经从调用面向客户端的 VM 的 UI 部分获得了大部分客户端详细信息,因此将其传入并避免再次访问数据库以获取详细信息。

您这里遇到的是 ViewModel 之间的通信问题。有很多解决方案,但我最喜欢的是中介模式:

using System;

namespace UnitTestProject2
{
    public class GetDataViewModel
    {
        IMediator mediator;
        public GetDataViewModel(IMediator mediator)
        {
            this.mediator = mediator;
            this.mediator.ListenFor("LoggedIn", LoggedIn);
        }
        protected string UserId;
        protected void LoggedIn(Object sender, EventArgs e)
        {
            UserId = sender.ToString();
        }
    }

    public class LoginViewModel
    {
        IMediator mediator;
        public LoginViewModel(IMediator mediator)
        {
            this.mediator = mediator;
        } 

        public string UserId { get; set; }
        public void Login(string userid)
        {
            this.UserId = userid;
            this.mediator.RaiseEvent("LoggedIn", this.UserId);
        }
    }

    public interface IMediator
    {
        public void ListenFor(string eventName, EventHandler action );
        public void RaiseEvent(string eventName, object data);
    }
}

我没有在这里实现调解器,因为它可能会涉及很多,并且有许多可用的包。但是你可以从我简单的界面中看到这个想法。本质上,Mediator 提供了一个全局的 EventHandlers 列表,任何 Viewmodel 都可以调用或添加它。您仍然有在何处存储事件名称的问题。在枚举中包含这些很好,但这会给您带来耦合问题。 (一个我通常忽略的问题)

或者你可以有一个控制器或(MasterViewModel,如果你喜欢 MVVM)

using System;

namespace UnitTestProject3
{
    public class GetDataViewModel
    {
        protected string UserId;
        public void LoggedIn(Object sender, EventArgs e)
        {
            UserId = sender.ToString();
        }
    }

    public class LoginViewModel
    {
        public EventHandler OnLogin;
        public string UserId { get; set; }
        public void Login(string userid)
        {
            this.UserId = userid;
            if (this.OnLogin != null)
            {
                this.OnLogin(this.UserId, null);
            }
        }
    }

    public class Controller // or MasterViewModel
    {
        public void SetUp()
        {
            GetDataViewModel vm1 = new GetDataViewModel();
            LoginViewModel vm2 = new LoginViewModel();
            vm2.OnLogin += vm1.LoggedIn;

            //wire up to views and display
        }
    }
}