【问题标题】:prism vs mvvm light for wpfwpf的棱镜vs mvvm灯
【发布时间】:2013-12-27 13:02:27
【问题描述】:

我们正在启动一个带有 MVVM 项目的 WPF,并且必须决定使用 PRISM 或 MVVM Light(我对这两个框架都是新手)。我已经阅读了一些帖子,但仍有一些问题。有人可以对以下方面进行一些说明吗?两个框架?:

  1. 性能:无论出于何种原因,其中一个框架的性能是否优于另一个?

  2. 应用程序内的通信(viewmodel 到 viewmodel 或模块之间等):我读过 MVVM Light 具有消息服务,这似乎也相当容易。但 PRISM 似乎没有任何等价物。真的吗? PRISM 将如何处理交互?

  3. 单元测试:已阅读 PRISM 更好地支持单元测试。我们还能在 MVVM Light 中编写 NUNIT 或 VSTS 测试吗?

【问题讨论】:

  • 回答您的 2):Prism 有一个 EventAggregator,它执行您描述的 MVVM 所做的事情。
  • 除了emedbo的评论,您可以在Loosely Coupled CommunicationPrism指南章节中找到更多Prism交流的相关信息 MSDN.
  • PRISM 的最佳功能之一是Regions。它有EventAggregator 用于ViewModelViewModel 通讯。您可以使用混合行为(System.Windows.Interactivity、Microsoft.Expression.Interactions)进行交互。单元测试取决于您编写ViewModel 单元测试的友好程度,而不是取决于您使用的 MVVM 框架。因此,如果您确实希望在您的应用程序中支持Region,那么请使用 PRISM,否则使用 MVVM-Light。不知道有没有办法也可以用 MVVM-Light 实现 Regions。

标签: wpf mvvm prism mvvm-light


【解决方案1】:
  1. 我刚刚将一个项目从 Prism 转移到 MvvmLight,它似乎工作得更快(非常主观)。

  2. Prism 和 MvvmLight 都有 Mediator 实现(Prism 中的 IEventAggregator,MvvmLight 中的 IMessenger)。但与 IEventAggregator 相比,IMessenger 具有更多功能(例如,使用令牌发送消息)并且使用起来更加方便(参见下一项)。

    MvvmLight 还有一个更强大的 ViewModelBase 类。

  3. 使用 MvvmLight 的应用程序比使用 Prism 的应用程序更容易测试。例如,IMessenger 比 IEventAggregator 更容易模拟。

PrismViewModel.cs

using System;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Prism.ViewModel;

// An ugly empty event class
public class StringEvent : CompositePresentationEvent<string> { }

public sealed class PrismViewModel : NotificationObject
{
    private readonly IEventAggregator _eventAggregator;

    private string _name;

    public PrismViewModel(IEventAggregator eventAggregator)
    {
        if (eventAggregator == null)
            throw new ArgumentNullException("eventAggregator");

        _eventAggregator = eventAggregator;
        _eventAggregator.GetEvent<StringEvent>().Subscribe(s => Name = s);
    }

    public string Name
    {
        get { return _name; }
        set
        {
            // boiler-plate code
            if (value == _name) 
                return;
            _name = value;
            RaisePropertyChanged(() => Name);
        }
    }

    public void SendMessage(string message)
    {
        _eventAggregator.GetEvent<StringEvent>().Publish(message);
    }
}

PrismViewModelTestCase.cs

using System;
using FluentAssertions;
using Microsoft.Practices.Prism.Events;
using NSubstitute;
using NUnit.Framework;

public class PrismViewModelTestCase
{
    private static PrismViewModel CreateViewModel(IEventAggregator eventAggregator = null)
    {
        // You can't return Substitute.For<IEventAggregator>()
        // because it returns null when PrismViewModel's constructor
        // invokes GetEvent<StringEvent>() method which leads to NullReferenceException
        return new PrismViewModel(eventAggregator ?? CreateEventAggregatorStub());
    }

    private static IEventAggregator CreateEventAggregatorStub()
    {
        var eventAggregatorStub = Substitute.For<IEventAggregator>();
        eventAggregatorStub.GetEvent<StringEvent>().Returns(Substitute.For<StringEvent>());
        return eventAggregatorStub;
    }

    [Test]
    public void Constructor_WithNonNullEventAggregator_ExpectedSubscribesToStringEvent()
    {
        // Arrange
        var stringEventMock = Substitute.For<StringEvent>();

        var eventAggregatorStub = Substitute.For<IEventAggregator>();
        eventAggregatorStub.GetEvent<StringEvent>().Returns(stringEventMock);

        // Act
        CreateViewModel(eventAggregatorStub);

        // Assert
        // With constrained isolation framework you can only mock virtual members
        // CompositePresentationEvent<TPayload> has only one virtual Subscribe overload with four parameters
        stringEventMock.Received()
                       .Subscribe(Arg.Any<Action<string>>(), Arg.Any<ThreadOption>(), Arg.Any<bool>(),
                                  Arg.Any<Predicate<string>>());
    }

    [Test]
    public void Name_ExpectedRaisesPropertyChanged()
    {
        var sut = CreateViewModel();
        sut.MonitorEvents();

        sut.Name = "any-value";

        sut.ShouldRaisePropertyChangeFor(vm => vm.Name);
    }

    [Test]
    public void SendMessage_ExpectedPublishesStringEventThroughEventAggregator()
    {
        // Arrange
        var stringEventMock = Substitute.For<StringEvent>();

        var eventAggregatorStub = Substitute.For<IEventAggregator>();
        eventAggregatorStub.GetEvent<StringEvent>().Returns(stringEventMock);

        var sut = CreateViewModel(eventAggregatorStub);
        const string expectedPayload = "any-string-payload";

        // Act
        sut.SendMessage(expectedPayload);

        // Assert
        stringEventMock.Received().Publish(expectedPayload);
    }
}

MvvmLightViewModel.cs

using System;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Messaging;

public sealed class MvvmLightViewModel : ViewModelBase
{
    private string _name;

    public MvvmLightViewModel(IMessenger messenger)
    {
        if (messenger == null)
            throw new ArgumentNullException("messenger");

        // ViewModelBase already have field for IMessenger
        MessengerInstance = messenger; 
        MessengerInstance.Register<string>(this, s => Name = s);
    }

    public string Name
    {
        get { return _name; }
        set { Set(() => Name, ref _name, value); // Chic!  }
    }

    public void SendMessage(string message)
    {
        MessengerInstance.Send(message);
    }
}

MvvmLightViewModelTestCase.cs

using System;
using FluentAssertions;
using GalaSoft.MvvmLight.Messaging;
using NSubstitute;
using NUnit.Framework;

public class MvvmLightViewModelTestCase
{
    private static MvvmLightViewModel CreateViewModel(IMessenger messenger = null)
    {
        return new MvvmLightViewModel(messenger ?? Substitute.For<IMessenger>());
    }

    [Test]
    public void Constructor_WithNonNullMessenger_ExpectedRegistersToStringMessage()
    {
        var messengerStub = Substitute.For<IMessenger>();

        var sut = CreateViewModel(messengerStub);

        messengerStub.Received().Register(sut, Arg.Any<Action<string>>());
    }

    [Test]
    public void Name_ExpectedRaisesPropertyChanged()
    {
        var sut = CreateViewModel();
        sut.MonitorEvents();

        sut.Name = "any-value";

        sut.ShouldRaisePropertyChangeFor(vm => vm.Name);
    }

    [Test]
    public void SendMessage_ExpectedSendsStringMessageThroughMessenger()
    {
        var messengerMock = Substitute.For<IMessenger>();
        var sut = CreateViewModel(messengerMock);
        const string expectedMessage = "message";

        sut.SendMessage(expectedMessage);

        messengerMock.Received().Send(expectedMessage);
    }
}

棱镜的缺点:

我认为任何新项目都应该基于现代解决方案和方法。 恕我直言,任何现代 MVVM 框架(如 Catel、Caliburn.Micro、MvvmLight、ReactiveUI)都比 Prism 好得多。

【讨论】:

  • 不再开发棱镜是完全错误的。
  • 是的,现在错了(不久前发布的新版 Prism)。当我回答这个问题时,Prism 的未来相当模糊。但是 IEventAggregator 仍然缺乏可测试性(我的回答仍然相关)。
  • 请注意,Prism 有一个BindableBase VM 基类,它提供了一个SetProperty() 方法来处理RaisePropertyChanged() 样板,即:名称的设置器将是:set { SetProperty(ref _name, value); }。这使得它几乎与 MVVM Light VM 相同
【解决方案2】:

我不相信 MS 曾经将 PRISM 推广为“框架”,就像 MVVM Light、Caliburn 等被“宣传”一样。我的理解是,PRISM 总是作为一种“实践”呈现给“世界”。我相信,这仅仅意味着构建应用程序的有组织的方式。由于 PRISM 提供的所有代码,它变得有点令人困惑,人们可以将其视为可用于构建应用程序的“框架”。而且,确实,您可以使用 PRISM 提供的代码来构建您自己的应用程序。但是,在我看来,PRISM 比可用于 MVVM 的“框架”复杂得多,而且学习曲线陡峭,并且可能会“过度杀伤”并使您的应用程序变得比必要的复杂。如果您在构建应用程序时有时间学习最新的“框架”或“实践”,那就太好了!我的经验是,我没有机会学习一些新的“实践”或最新的“框架”,但必须完成工作。在一个理想的世界里,人们希望能够使用最新最好的“框架”或采用最新的“实践”,但有时你只需要坚持你已经知道的并完成它。

【讨论】:

  • 嗨,欢迎来到堆栈溢出。我不确定这是否“回答”了这里的“问题”(也......过度使用“吓人的引号”会使这更难“阅读”;))。原始海报提出了三个非常具体的问题,您的回复中没有解决这些问题。也许你也可以解决这些问题?否则,我会将您在上面写的内容归类为评论而不是答案……这意味着它更正确地放在评论部分(就在原始问题的下方)。现在 - 你还没有足够的代表来发表评论......但如果答案只是评论,它会被评论者迅速删除。
  • @TarynEast 尚未被删除 =)
  • 你想要我吗?我能。 ;) 注意:您现在有足够的代表将其移至 cmets 部分...
【解决方案3】:

您无法完全比较 Prism 和 MvvmLight。

尽管 Prism 被称为 MVVM 框架,但 Prism 更多的是关于应用程序架构。实际上,直到 Prism 5 它与 MVVM 无关,并且在 Prism 4.1 和之前的版本中没有 BaseViewModel 类。

Prism 不是 MVVM 框架,它是应用程序框架,它位于更高的位置。 Prism 5 引入了对 MVVM 的一些支持,而 Prism 6 则更进一步。

MVVM 只是 prism 提供解决问题的另一个方面。

这就像比较 Angular 和 Knockout。 AngularJS 管理整个应用程序并定义应用程序代码的结构指南,而使用 KnockoutJS,应用程序结构完全取决于您。 Prism 和 MvvmLight 的情况类似。

Prism 提供了一组设计模式的实现,这些设计模式有助于编写结构良好且可维护的 XAML 应用程序,包括 MVVM、依赖注入、命令、事件聚合等。 Prism 的核心功能是针对这些平台的可移植类库中的共享代码库; WPF、Windows 10 UWP 和 Xamarin 表单。

如果您正在使用 wpf 开发企业级应用程序,我建议您使用 Prism。

请观看有关使用 Prism 简化 MVVM 的网络研讨会。主持人是布赖恩·拉古纳斯: https://www.youtube.com/watch?v=ZfBy2nfykqY

【讨论】:

    猜你喜欢
    • 2011-02-15
    • 2015-12-20
    • 1970-01-01
    • 2017-03-26
    • 1970-01-01
    • 2012-01-04
    • 1970-01-01
    • 2018-11-10
    • 2011-04-02
    相关资源
    最近更新 更多