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
}
}
}
我目前在我的应用程序中传递 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
}
}
}