WPF MVVM 中 ViewModel 的 Mediatr 通知
Mediatr Notifications on ViewModel in WPF MVVM
在实现 WPF 应用程序时,我偶然发现了我的应用程序在每个 ViewModel 中都需要一些全局数据的问题。但是,一些 ViewModel 只需要读取权限,而其他 ViewModel 需要 read/write 访问此字段。起初,我偶然发现了 Microsoft 的 SessionContext 想法,如下所示:
public class SessionContext
{
#region Public Members
public static string UserName { get; set; }
public static string Role { get; set; }
public static Teacher CurrentTeacher { get; set; }
public static Parent CurrentParent { get; set; }
public static LocalStudent CurrentStudent { get; set; }
public static List<LocalGrade> CurrentGrades { get; set; }
#endregion
#region Public Methods
public static void Logon(string userName, string role)
{
UserName = userName;
Role = role;
}
public static void Logoff()
{
UserName = "";
Role = "";
CurrentStudent = null;
CurrentTeacher = null;
CurrentParent = null;
}
#endregion
}
这不是(至少在我看来)可以很好地测试,并且如果我的全局数据增长它会出现问题(我认为这可能会发生在这个应用程序中)。
我发现的下一件事是 Mediator/the 调解器模式的实现 link。我喜欢 Norbert 的设计理念,并考虑为我的项目实施类似的东西。然而,在这个项目中,我已经在使用令人印象深刻的 Mediatr Nuget 包,它也是一个 Mediator 实现。所以我想 "Why reinvent the Wheel" 如果我可以使用一个好的且经过良好测试的 Mediator。但这里开始我真正的问题:如果其他 ViewModel 将对全局数据的更改发送到我的只读 ViewModel,我将使用通知。这意味着:
public class ReadOnlyViewModel : NotificationHandler<Notification>
{
//some Member
//global Data
public string Username {get; private set;}
public async Task Handle(Notification notification, CancellationToken token)
{
Username = notification.Username;
}
}
现在的问题:
1. 这是使用MVVM的好实践吗(只是感觉这样做是错误的,因为感觉像是在ViewModel中暴露了业务逻辑)
2. 有没有更好的方法来分离它,这样我的 Viewmodel 就不需要继承 5 到 6 个不同的 NotificationHandlers<,>?
更新:
作为对我想在这里实现的目标的澄清:
我的目标是实现一个 wpf 应用程序,该应用程序为它的 Window 之一管理一些全局数据(比如说上面提到的用户名)。这意味着因为我使用的是 DI 容器(并且因为它是什么类型的数据)所以我必须声明服务 @mm8 提议为单例。然而,如果我需要打开一个新的 Window,此时需要不同的全局数据,那会有点问题(我有这种情况)。这意味着我要么需要将生命周期更改为 "kind of scoped" 之类的东西,要么(打破 class 的单一职责)通过为不同的目的添加更多字段,或者我为 n 个可能的 Windows 我可能需要打开。对于拆分服务的第一个想法:我想这样做,因为这会减轻上述所有问题,但这会使数据共享出现问题,因为我不知道将这些全局数据从 Writeservice 传递到readservice,而异步或并行 运行 正在后台线程中发生,这可能会触发 writeservice 更新它的数据。
您可以使用注入视图模型的共享服务。例如,它可以实现两个接口,一个用于写操作,一个只用于读操作,例如:
public interface IReadDataService
{
object Read();
}
public interface IWriteDataService : IReadDataService
{
void Write();
}
public class GlobalDataService : IReadDataService, IWriteDataService
{
public object Read()
{
throw new NotImplementedException();
}
public void Write()
{
throw new NotImplementedException();
}
}
然后您将注入应该具有 IWriteDataService
写访问权限的视图模型(以及其他具有 IReadDataService
的视图模型):
public ViewModel(IWriteDataService dataService) { ... }
该解决方案既使代码易于理解又易于测试。
在实现 WPF 应用程序时,我偶然发现了我的应用程序在每个 ViewModel 中都需要一些全局数据的问题。但是,一些 ViewModel 只需要读取权限,而其他 ViewModel 需要 read/write 访问此字段。起初,我偶然发现了 Microsoft 的 SessionContext 想法,如下所示:
public class SessionContext
{
#region Public Members
public static string UserName { get; set; }
public static string Role { get; set; }
public static Teacher CurrentTeacher { get; set; }
public static Parent CurrentParent { get; set; }
public static LocalStudent CurrentStudent { get; set; }
public static List<LocalGrade> CurrentGrades { get; set; }
#endregion
#region Public Methods
public static void Logon(string userName, string role)
{
UserName = userName;
Role = role;
}
public static void Logoff()
{
UserName = "";
Role = "";
CurrentStudent = null;
CurrentTeacher = null;
CurrentParent = null;
}
#endregion
}
这不是(至少在我看来)可以很好地测试,并且如果我的全局数据增长它会出现问题(我认为这可能会发生在这个应用程序中)。 我发现的下一件事是 Mediator/the 调解器模式的实现 link。我喜欢 Norbert 的设计理念,并考虑为我的项目实施类似的东西。然而,在这个项目中,我已经在使用令人印象深刻的 Mediatr Nuget 包,它也是一个 Mediator 实现。所以我想 "Why reinvent the Wheel" 如果我可以使用一个好的且经过良好测试的 Mediator。但这里开始我真正的问题:如果其他 ViewModel 将对全局数据的更改发送到我的只读 ViewModel,我将使用通知。这意味着:
public class ReadOnlyViewModel : NotificationHandler<Notification>
{
//some Member
//global Data
public string Username {get; private set;}
public async Task Handle(Notification notification, CancellationToken token)
{
Username = notification.Username;
}
}
现在的问题: 1. 这是使用MVVM的好实践吗(只是感觉这样做是错误的,因为感觉像是在ViewModel中暴露了业务逻辑) 2. 有没有更好的方法来分离它,这样我的 Viewmodel 就不需要继承 5 到 6 个不同的 NotificationHandlers<,>?
更新: 作为对我想在这里实现的目标的澄清: 我的目标是实现一个 wpf 应用程序,该应用程序为它的 Window 之一管理一些全局数据(比如说上面提到的用户名)。这意味着因为我使用的是 DI 容器(并且因为它是什么类型的数据)所以我必须声明服务 @mm8 提议为单例。然而,如果我需要打开一个新的 Window,此时需要不同的全局数据,那会有点问题(我有这种情况)。这意味着我要么需要将生命周期更改为 "kind of scoped" 之类的东西,要么(打破 class 的单一职责)通过为不同的目的添加更多字段,或者我为 n 个可能的 Windows 我可能需要打开。对于拆分服务的第一个想法:我想这样做,因为这会减轻上述所有问题,但这会使数据共享出现问题,因为我不知道将这些全局数据从 Writeservice 传递到readservice,而异步或并行 运行 正在后台线程中发生,这可能会触发 writeservice 更新它的数据。
您可以使用注入视图模型的共享服务。例如,它可以实现两个接口,一个用于写操作,一个只用于读操作,例如:
public interface IReadDataService
{
object Read();
}
public interface IWriteDataService : IReadDataService
{
void Write();
}
public class GlobalDataService : IReadDataService, IWriteDataService
{
public object Read()
{
throw new NotImplementedException();
}
public void Write()
{
throw new NotImplementedException();
}
}
然后您将注入应该具有 IWriteDataService
写访问权限的视图模型(以及其他具有 IReadDataService
的视图模型):
public ViewModel(IWriteDataService dataService) { ... }
该解决方案既使代码易于理解又易于测试。