Ответ 1
-
Я просто перенес проект из Prism в MvvmLight и, похоже, работает быстрее (очень субъективно).
-
Оба Prism и MvvmLight имеют реализацию Медиатора (IEventAggregator в Prism, IMessenger в MvvmLight). Но у IMessenger больше возможностей (например, отправка сообщений с токенами) по сравнению с IEventAggregator и гораздо удобнее использовать (см. Следующий пункт).
MvvmLight также имеет более мощный класс ViewModelBase.
-
Приложения, использующие 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 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);
}
}
Недостатки Призмы:
-
не полностью открытый проект (официальный репозиторий Prism доступен только для чтения)- 2015-10-30: теперь он полностью открыт: https://github.com/PrismLibrary/Prism
-
он больше не активно развивается- 2015-10-30: новая версия Prism: https://github.com/PrismLibrary/Prism
- прямое использование его классов приводит к котельной плите и менее читаемому коду.
Я думаю, что любой новый проект должен основываться на современных решениях и подходах. IMHO, любой современный MVVM-каркас (например, Catel, Caliburn.Micro, MvvmLight, ReactiveUI) намного лучше, чем Призма.