prism vs mvvm light for wpf

2019-03-09 08:45发布

We are starting a WPF with MVVM project and have to decide on PRISM or MVVM Light (I am new to both these frameworks). I have read through a few posts but still have a few questions. Can someone please throw some light on the following aspects w.r.t. both the frameworks?:

  1. Performance: Will either one framework perform better than the other for any reason?

  2. Communication within the app (viewmodel to viewmodel or between modules etc): I have read that MVVM Light has Messenging Service which appears to be fairly easy as well. But PRISM does not appear to have any equivalent. Is that true? How would PRISM handle interactions?

  3. Unit Testing: Have read that PRISM supports Unit Testing better. Can we still write NUNIT or VSTS tests in MVVM Light also?

3条回答
干净又极端
2楼-- · 2019-03-09 09:12

I don't believe MS has ever promoted PRISM as a "framework" in the same sense that MVVM Light, Caliburn, etc. are "advertised." My understanding is the PRISM was always presented to the "world" as a "practice." Which, I believe, simply means a organized way of building applications. It becomes a bit confusing because of all the code that is supplied with PRISM and one can consider it a "framework" that can be used to build applications. And, it's true, you can use the code that is supplied with PRISM to build applications of your own. But, PRISM, in my mind is much more complicated than the "frameworks" that are available for MVVM and there is a steep learning curve as well as the possibility of "overkill" and making your application more complex than is necessary. If you have the time to learn the latest "framework" or "practice" at the time you are building your application, that's GREAT! My experience has been that I don't have the luxury of factoring in learning some new "practice" or the latest "framework" but have to get the job done. In an ideal world, one would like to be able to use the latest and greatest "framework" or employ the latest "practices" but sometimes you just have to stick with what you already know and get it done.

查看更多
干净又极端
3楼-- · 2019-03-09 09:15
  1. I just moved a project from Prism to MvvmLight and it seems to work faster (very subjective).

  2. Both Prism and MvvmLight have Mediator realisation (IEventAggregator in Prism, IMessenger in MvvmLight). But IMessenger has more abilities (for instance, sending messages with tokens) compared to IEventAggregator and is much more convenient to use (see next item).

    MvvmLight also has a more powerful ViewModelBase class.

  3. Applications that use MvvmLight are much easier to test than those that use Prism. For instance, IMessenger is easier to mock than 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);
    }
}

Disadvantages of Prism:

I think that any new project should be based on modern solutions and approaches. IMHO, any modern MVVM-framework (like Catel, Caliburn.Micro, MvvmLight, ReactiveUI) is much better than Prism.

查看更多
虎瘦雄心在
4楼-- · 2019-03-09 09:27

You cannot fully compare Prism and MvvmLight.

Prism is more about application architecture even though Prism has been known as MVVM framework. Actually until Prism 5 it had nothing to do with MVVM and It didn't have BaseViewModel class in Prism 4.1 and in prior.

Prism is not a MVVM framework it is application framework it sits higher than that. Prism 5 introduced some support for MVVM and Prism 6 took it futher.

MVVM is just another aspect of problems that prism provides guidance to solve.

It is like comparing Angular vs. Knockout. AngularJS manages the whole application and defines guidelines on how the application code should be structured, whereas with KnockoutJS the application structure is entirely up to you. It is a similar case between Prism and MvvmLight.

Prism provides an implementation of a collection of design patterns that are helpful in writing well structured and maintainable XAML applications, including MVVM, dependency injection, commanding, event aggregation, and more. Prism's core functionality is a shared code base in a Portable Class Library targeting these platforms; WPF, Windows 10 UWP, and Xamarin Forms.

I would recommend to use Prism if you are developing an enterprise level application using wpf.

Please watch this Webinar about MVVM Made Simple with Prism. The presenter is Brian Lagunas: https://www.youtube.com/watch?v=ZfBy2nfykqY

查看更多
登录 后发表回答