What is the proper usage of Activation/Deactivation in conjunction with ObservableAsPropertyHelper? Given a view and viewmodel that reflects long lived (hot) observables, the subscription would need to be disposed when the view and viewmodel is unloaded. However ObservableAsPropertyHelper, which is recommended to be readonly is assigned in the constructor of the viewmodel, and cannot be part of the activation/deactivation lifecycle. What is the right way to handle these kind of situations?
public interface ILongLivedObject
{
IObservable<bool> Status { get; }
}
public class TestViewModel : ReactiveObject
{
private readonly ObservableAsPropertyHelper<bool> _status;
public bool Status => _status.Value;
public TestViewModel(ILongLivedObject obj)
{
_status = obj.Status.ToProperty(this, vm => vm.Status); //how is the subscription disposed?
}
}
This also gets me into a corner when trying to add commands that depends on this status. In my application, a common use case is to have some hardware that is on some specific status (e.g. IsOpen) and allow commands when it is true.
Without knowing better, this is what I am trying to do:
public class TestViewModel : ReactiveObject
{
private readonly ObservableAsPropertyHelper<bool> _status;
public bool Status => _status.Value;
public ReactiveCommand<Unit, Unit> DoStuff {get;}
public TestViewModel(ILongLivedObject obj)
{
_status = obj.Status.ToProperty(this, vm => vm.Status); //how is the subscription disposed?
DoStuff = ReactiveCommand.CreateFromTask(....., this.WhenAnyValue(this, x => x.Status);
}
}
If I try to move the _status creation into this.WhenActivated, the app will crash as the command is trying to get the value of status before it is created. Am I supposed to (re)create the comand during activation? This seems wrong and pretty costly?
So far, it seems better to have a regular Status property with a protected setter and make a regular subscription in this.WhenActivated - but this is what the handbook tells to avoid for "readonly" properties.
So one thing to be aware of in Reactive programming, disposing often means "unsubscribe".
You often don't need to unsubscribe since the garbage collector will take care of it for you, providing you create ObservableAsPropertyHelper (abbreviated as OAPH) only with observables generated from the current ViewModel.
In your case however, your observable/object, is related to a object outside the current ViewModel. The OAPH itself is a Disposable object.
So you can use ISupportsActivation (shortly going to have a replacement of IActivableViewModel) and pass your OAPH into it's Disposable property.
public class TestViewModel : ReactiveObject, ISupportsActivation
{
private readonly ObservableAsPropertyHelper<bool> _status;
public bool Status => _status.Value;
public ViewModelActivator Activator { get; } = new ViewModelActivator();
public TestViewModel(ILongLivedObject obj)
{
_status = obj.Status.ToProperty(this, vm => vm.Status);
this.WhenActivated(disposables =>
{
disposables(_status);
}
}
}
The disposables parameter passed into the WhenActivated lambda is a Func that takes a IDisposable
In the view, make sure you derive off IActivatable (soon to be renamed IActivatableView) and use WhenActivated in the constructor of the view as well.
Related
I need to call an async method ConnectToLibrary once a book is selected, meaning once SelectedBook property is set. I saw many solutions. But all of them require hacks or starting a task/thread from a setter. My question, what's wrong with using a mediator like Messenger from Microsoft Toolkit.Mvvm?
I can just register a callback to ConnectToLibrary with Messenger.Register<...BookSelectedMessage>(...ConnectToLibrary...) and then call Messenger.Send(new BookSelectedMessage()) from the SelectedBook's setter.
Is this the correct way to do this in MVVM?
public MyViewModel : ObservableRecipient
{
public ObservableCollection<BookInfo> Books { get; } = new();
private BookInfo _selectedBook;
public BookInfo SelectedBook
{
get => _selectedBook;
set => SetProperty(ref _selectedBook, value);
}
private async Task ConnectToLibrary(BookInfo info)
{
await START_SOME_ASYNC_METHOD();
}
}
Nothing. In either case any Exception is swallowed. And since you are asking, what's wrong with starting a thread from a Setter? It sounds to me like the requirements of your app require this anyway, no?
Often in my applications built with Caliburn Micro I have a need to store some global data; this could be app specific config, authentication properties, etc. I generally put them in a class called "Session" and inject that via constructor injection so that every view model has a reference to a single instance of Session.
I found a case where I wanted a guard method on two different view models to be linked to a Session variable; the issue is guard methods are generally notified of changes in the setter of the changed variable. Since it's a global, it doesn't know what depends on it. (It occurs to me that this pattern of variables being aware of what guard is hooked into them is bad, but when it's all in the same ViewModel it doesn't matter much.)
I could throw an event, but that's messy and a lot of work for something that should be simple.
I could try to identify every spot where it may have been updated and manually notify, but that's error prone.
public class MyViewModel: Screen{
public MyViewModel(SessionInfo session){
Session = session;
}
public CanTakeAction { get { return !string.isNullOrWhitespace(Session.SomeProperty); } }
}
public class SessionInfo {
public SessionInfo(){}
public string SomeProperty { get; set; }
// this is where I would normally notify a guard method, but this is not going to work
NotifyOfPropertyChange(() => CanTakeAction); // except it doesn't know about CanTakeAction
}
One possible solution would be to introduce a base ViewModel, which has the guard methods (virtual). For Example,
public class ViewModelBase:Screen
{
private SessionInfo _sessionInfo;
public ViewModelBase(SessionInfo sessionInfo)
{
_sessionInfo = sessionInfo;
}
public void NotifyGuardMethods()
{
NotifyOfPropertyChange(nameof(CanTakeAction));
}
public virtual bool CanTakeAction { get; set; } = false;
}
For all the ViewModels that needs to be notified by the change in Session, you could now derieve from the ViewModelBase.
public class ShellViewModel:ViewModelBase
{
public override bool CanTakeAction { get=>//its own logic; set=>//its own logic; };
}
You could now introduce Events to the ViewModelBase, which could use the NotifyGuardMethods defined in the base class to notify all other view models. This ensures the messsy Events part would be restricted to one class alone (base view model).
I've implemented the command pattern (in a multi-support way) in my application.
Structure:
class MultiCommand : BaseCommand
abstract class BaseCommand : ICommand
Process Flow:
var commandsGroup = new MultiCommand(new List<ICommand>()
{
new Command1(),
new Command2(),
new Command3(),
});
commandsGroup.Execute()
Now, suppose that in Command1 a somethingID is changed and I'll use this new value in Command2... And also, that there are plenty of other properties and objects that are being affected during the whole execution process.
Also, there are some interface implementations that should be available at any command just using the context object like:
Context.ServerController.something();
The instantiation of the IServerController will take place just before the multiCommandGroup initialization.
How can I have a shared context like this for all Commands of the group?
Example of the Context class:
public class CommandContext
{
public IServerController ServerController;
public RequiredData Data { get; set; }
public CommandContext(){}
}
IMPORTANT
A minimal implementation Code is here
1) If you want to keep this interface, then you have to pass this context as constructor parameter:
new MultiCommand(new List<ICommand>()
{
new Command1(context),
new Command2(context),
new Command3(context),
})
2) As another option you can accept list of delegates instead of list of commands.
MultiCommand will be look like this:
class MultiCommand : ICommand
{
public MultiCommand(List<Func<Context, Command>> commands, Context context)
}
That is almost the same except MultiCommand is responsible for all the commands share the same context.
3) Looks like commands in MultiCommand depends on result of previous command. In this case Command pattern is not probably the best. Maybe you should try to implement Middleware chain here?
interface IMiddleware<TContext>
{
void Run(TContext context);
}
class Chain<TContext>
{
private List<IMiddleware<TContext>> handlers;
void Register(IMiddleware<TContext> m);
public void Run(TContext context)
{
handlers.ForEach(h => h.Run(context));
}
}
I would suggest to make somethings generic. Here is a super simple example.
class MultiCommand<TContext>
{
List<Command<TContext>> Commands;
TContext Context;
}
You could have a constructor on your BaseCommand class (and its derived classes) that would accept a Context class of some kind. When instantiating the commands that will belong to the same group, you could provide them all the same context object. Maybe something like:
public class CommandContext
{
// The object that will be the target of the commands' actions.
public object Data { get; set; }
// ... any other properties that might be useful as shared state between commands...
}
public abstract class BaseCommand : ICommand
{
protected CommandContext Context { get; private set; }
public BaseCommand(CommandContext ctx)
{
Context = ctx;
}
}
public class ChangeSomethingIDCommand : BaseCommand
{
public ChangeSomethingIDCommand(CommandContext ctx) : base(ctx)
{ }
public void Execute()
{
var target = (SomeDomainClass)Context.Data;
target.SomethingID++;
}
}
// Elsewhere in your code (assuming 'myTargetDomainClassInstance' is
// a SomeDomainClass instance that has been instantiated elsewhere and
// represents the object upon which the commands will do work):
var ctx = new CommandContext { Data = myTargetDomainClassInstance };
var commandGroup = new MultiItemCommand(ctx, new List<ICommand>
{
new ChangeSomethingIDCommand(ctx),
new Command2(ctx),
new Command3(ctx)
});
commandGroup.Execute();
Consider a Functional Style
public class SomeMainClass{
public void MultiCommandInit()
{
MultiCommand.New()
.Add(new Command1())
.Add(new Command2())
.Add(new Command3())
.SharedContext(CC => {
CC.Data = new RequiredData();
CC.ServerController = GetServerController();
});
}
private IServerController GetServerController()
{
// return proper instance of server controller
throw new NotImplementedException();
}
}
Requires this extension method / function...
public static class XMultiCommand
{
// How can I have a shared context like this for all Commands of the group?
public static MultiCommand SharedContext(this MultiCommand mc, Action<CommandContext> CallBack)
{
var cc = new CommandContext();
CallBack(cc);
mc.SharedContext = cc;
return mc;
}
}
Finally, these changes to MultiCommand
public class MultiCommand
{
private System.Collections.Generic.List<ICommand> list;
public List<ICommand> Commands { get { return list; } }
public CommandContext SharedContext { get; set; }
public MultiCommand() { }
public MultiCommand(System.Collections.Generic.List<ICommand> list)
{
this.list = list;
}
public MultiCommand Add(ICommand cc)
{
list.Add(cc);
return this;
}
internal void Execute()
{
throw new NotImplementedException();
}
public static MultiCommand New()
{
return new MultiCommand();
}
}
Cool Things Happen Using Functional Styles
Re-usability soars!
Hyper focus on Single Responsibility concerns
Composition becomes the Norm
Code Maintenance becomes simple
Intellisense becomes your built-in API (just use code commenting)
No radical OOP design patterns are needed
Fluent code becomes very enjoyable to work with
Nested / Decorated Functions are much more easy to imagine and implement
You will never repeat youerself
The Open/Closed principal becomes your religion
Code is now always Clear, Complete and Concise
Some even say no interfaces are needed any longer
In your case, going with injecting context through constructor is fine as mentioned by others. But in general, I would go with injecting the context through method parameters instead:
public class Command1: BaseCommand
{
//inject as parameter instead
public void Execute(Context ctx)
{
}
}
The reasons are:
The context should be managed by CommandGroup so that we have better encapsulation.
The CommandGroup is responsible for executing its list of commands so that it's possible for the CommandGroup to pass to each Command only the parameters each Command really needs, these parameters may be constructed at runtime (maybe by previous Commands) so that it's not possible to pass in these objects as the time we construct the list of commands. Therefore, it's easier to reuse Command and also simplify unit testing these Commands as we don't need to construct the whole context object in unit tests.
Maybe you don't need to care about these things at the moment, but method injection gives more flexibility. If you have worked with some frameworks in .NET, you would see something similar like OwinContext, FilterContext,.. they are passed as parameters and contain relevant information for that context.
In my opinion, your case is not a good fit for Command pattern. A Command represents a user request (action) and these objects could be created dynamically at runtime, but you're predefining your Commands at coding time.
What you're trying to do looks like owin middleware or asp.net web api message handler which are http://www.dofactory.com/net/chain-of-responsibility-design-pattern
And what about changing your approach? I did an architecture for DDD recently and executing a commad implies atomic operation (retrieve aggregate root from persitence, apply domain rules and pesist the aggregate) so I do not in needed of a share context and can batch multiple commands whithout worries.
Here you have an cqrs architecture that use command pattern with the above strategy I posted.
My 0.02:
1) The MultiCommand class looks like a Composite pattern.
You may want to add a GetParentCommand() method at the base command class and add an AddChildCommand() method at the MultiCommand class, which set every children's parent.
Then the children commands could get the context object from its parent. (Context object should also be defined in base class. And it may be of generic type.)
edit:
abstract class BaseCommand<T> : ICommand
{
public T Context { get; set; }
public BaseCommand Parent { get; set; }
}
class MultiCommand : BaseCommand
{
public void AddChildCommand(BaseCommand command)
{
command.parent = this; // we can get parent's context from children now
// put the command in an internal list
}
}
var commandsGroup = new MultiCommand();
commandsGroup.AddChildCommand(new Command1());
commandsGroup.AddChildCommand(new Command2());
commandsGroup.AddChildCommand(new Command3());
commandsGroup.Execute()
2) We may create a global singleton context object. In MultiCommand's Execute function, we could set the current context object before executing children's Execute function. Then child command could just access the singleton context object. And after all children's execution, the MultiCommand could reset the context. (The context is actually a stack here.)
edit:
abstract class BaseCommand : ICommand
{
// it could be put anywhere else as long as it can be accessed in command's Execute
// it can also be a stack
public static CommandContext Context {get; set;}
}
class MutliCommand : BaseCommand
{
public void Execute()
{
// do something to BaseCommand.Context
ChildCommand.Execute();
// do something to BaseCommand.Context
}
}
class ChildComand: BaseCommand
{
void Execute()
{
// do something with BaseCommand.Context
}
}
Another option is to put the context object as a parameter of the Execute function:
class MultiCommand : BaseCommand
{
void Execute(CommandContext context)
{
Children.Execute(context);
}
}
Suppose I have an object that observes an IObservable so that it's always aware of the current state of some external source. Internally my object has a method that uses that external value as part of the operation:
public class MyObject
{
public MyObject(IObservable<T> externalSource) { ... }
public void DoSomething()
{
DoSomethingWith(CurrentT);
}
}
What's the idomatic 'reactive' way of using IObservable for 'tracking current state' instead of 'responding to stream of events'.
Idea #1 is to just monitor the observable and write down values as they come in.
public class MyObject
{
private T CurrentT;
public MyObject(IObservable<T> externalSource)
{
externalSource.Subscribe((t) => { CurrentT = t; });
}
public void DoSomething()
{
DoSomethingWith(CurrentT);
}
}
And that's fine, but keeping track of the state in a class member seems very un-reactive-y.
Idea #2 is to use a BehaviorSubject
public class MyObject
{
private readonly BehaviorSubject<T> bs;
public MyObject(BehvaiorSubject<T> externalSource)
{
this.bs = externalSource
}
public void DoSomething()
{
DoSomethingWith(bs.Value);
}
}
But using subjects directly seems to be frowned upon. But at least in this case I have the ability to use a readonly field to store the behaviorsubject.
The BehaviorSubject (or ReplaySubject) does seem like it was made for this purpose, but is there some other better way here? And if I should use the subject, would it make more sense to take the subject as an injected parameter, or take the original observable and build the subject locally in the constructor?
(by the way I'm aware about the need to deal with the 1st value if the source observable hasn't fired yet. Don't get hung up on that, that's not what I'm asking about)
I'd go with a generic solution utilizing the ReactiveUI library. RUI has a standard way of mapping IObservable<T> to an INotifyPropertyChanged stateful property.
public class ObservableToINPCObject<T> : ReactiveObject, IDisposable
{
ObservableAsPropertyHelper<T> _ValueHelper;
public T Value {
get { return _ValueHelper.Value; }
}
public ObservableToINPCObject(IObservable<T> source, T initial = default(T))
{
_ValueHelper = source.ToProperty(this, p=>p.Value, initial);
}
public Dispose(){
_ValueHelper.Dispose();
}
}
ValueHelper is contains both the current state of the observable and automatically triggers the correct INPC notification when the state changes. That's quite a bit of boiler plate handled for you.
and an extension method
public static class ObservableToINPCObject {
public static ObservableToINPCObject<T> ToINPC<T>
( this IObservable<T> source, T init = default(T) )
{
return new ObservableToINPCObject(source, init);
}
}
now given an
IObservable<int> observable;
you can do
var obj = observable.ToINPC(10);
and to get the latest value
Console.WriteLine(obj.Value);
also given that Value is an INPC supporting property you can use it in databinding. I use ToProperty all the time for exposing my observables as properties for WPF databinding.
To be Rx-ish I'd suggest avoiding the second option and go with your first, but modified in one of two ways.
Either (1) make your class disposable so that you can cleanly close off the subscription to the observables or (2) make a method that lets you clean up individual observables.
(1)
public class MyObject : IDisposable
{
private T CurrentT;
private IDisposable Subscription;
public MyObject(IObservable<T> externalSource)
{
Subscription = externalSource
.Subscribe((t) => { CurrentT = t; });
}
public void Dispose()
{
Subscription.Dispose();
}
public void DoSomething()
{
DoSomethingWith(CurrentT);
}
}
(2)
public class MyObject
{
private T CurrentT;
public IDisposable Observe(IObservable<T> externalSource)
{
return externalSource
.Subscribe((t) => { CurrentT = t; });
}
public void DoSomething()
{
DoSomethingWith(CurrentT);
}
}
Both allow proper clean-up and both don't use a subject.
In my current project I've faced the following situation:
VM1 is used to be shown on a screen.
VM1 has a public property of VM2.
VM1 has a public property of VM3.
VM3 has a propertry that depends on VM2.
VM1 has no disposing mechanism.
At the beginning I thought about hooking to VM2.PropertyChanged event to check for the property I want and change the VM3 affected property accordingly, as:
public class VM1 : INotifyPropertyChanged
{
public property VM2 VM2 { get; private set; }
public property VM3 VM3 { get; private set; }
public VM1()
{
this.VM2 = new VM2();
this.VM3 = new VM3();
this.VM2.PropertyChanged += this.VM2_PropertyChanged;
}
private void VM2_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// if e.PropertyName equals bla then VM3.SomeProperty = lol.
}
}
This means that, since I can not unhook the event in this class, I have a memory leak.
So I end up passing an Action to VM2 so that it will be called when its important property changes the value, as:
public class VM2 : INotifyPropertyChanged
{
public Action WhenP1Changes { get; set; }
private bool _p1;
public bool P1
{
get
{
return _p1;
}
set
{
_p1 = value;
this.WhenP1Changes();
this.PropertyChanged(this, new PropertChangedEventArgs("P1");
}
}
}
public class VM1 : INotifyPropertyChanged
{
public VM2 VM2 { get; private set; }
public VM3 VM3 { get; private set; }
public VM1()
{
this.VM2 = new VM2();
this.VM3 = new VM3();
this.VM2.WhenP1Changes = () => VM3.SomeProperty = "lol";
}
}
Do I have a memory leak here?
PD: It would be great if you can also answer to:
- Is this even a good practice?
Thanks
Do I have a memory leak here?
The lambda assigned to VM2.WhenP1Changes captures this VM1 instance (needed to access the VM3 property), so as long as the view model VM2 is alive, it will keep VM1 alive. Whether this ends up being a leak depends on the lifecycle of those view models, but the implications are effectively the same as your first example using events.
In short, I would prefer to use a delegate to notify between view models. If you have a parent view model which has access to the various child view models (it seems that you do), then you can use one or more delegates like events to notify each other when updates are required.
Rather than typing out the whole scenario again, I'd prefer to point you towards my answer to the Passing parameters between viewmodels question, which provides a full description and code examples of this technique.
There is also a further addition that may interest you that can be found in my answer to the If necessary, how to call functions in MainViewViewModel from other ViewModels question. Please let me know if you have any questions.