- The need of Dispatcher in the ViewModel make it harder to unit test.
- Unit testing an asynchronous ViewModel is hard and tricky.
By extending uNhAddins ComponentBehavior I can make my ViewModel easy to unit test. Because the asynchronous behavior can be turn ON and OFF. Turn it ON when used in the real application and turn it OFF when used in the unit testing.
Here is the code:
[Behavior(1)] public class AsynchronousBehavior : IInterceptor { SynchronizationContext Context; public AsynchronousBehavior() { Context = SynchronizationContext.Current ?? new SynchronizationContext(); } public void Intercept(IInvocation invocation) { object[] attributes = invocation .Method.GetCustomAttributes(true); if (attributes.Any(x => x is UIThreadAttribute)) { Context.Post(o => invocation.Proceed(), null); return; } if (attributes.Any(x => x is BackgroundThreadAttribute)) { ThreadPool.QueueUserWorkItem(o => invocation.Proceed(), null); return; } invocation.Proceed(); } }
Pretty simple code eh? but how to use it? here it is:
public class MyAsyncViewModel { //Property keep simple using //NotifyPropertyChangedBehavior public virtual IListMyProperty { get; set; } [BackgroundThread] public virtual void RunLongTask() { //Long runing task called here. UpdateProperty(result); } [UIThread] protected virtual void UpdateProperty(string result) { //change collection require in UI thread MyProperty.Remove(0); } }
See, ViewModel is free form Dispatcher. The UIThread and BackgroundThread is only a simple attribute
public class UIThreadAttribute : Attribute {} public class BackgroundThreadAttribute: Attribute {}
But how the magic is work? in uNhAddIns everything is only a matter of the Guy Wire:
public void Install(IWindsorContainer container, IConfigurationStore store) { container.Register(Component.For().LifeStyle.Transient); var behaviorStore = new BehaviorDictionary(); behaviorStore.For () .Add () .Add (); container.Register(Component.For ().Instance(behaviorStore)); container.Register(Component.For ().LifeStyle.Transient); }
How the ON and OFF work? just remove the AsynchronousBehavior in the BehaviorDictionary registration then your ViewModel will work synchronously.
I really appreciate people work at uNhAddIns, seems like the library built with an ancient manuscript. You only need to spell simple script then a great thing happen.
NOTE:
Using [UIThread] actually not necessary, in fact accessing ViewModel property from different thread is OK but change collection from different thread is causing exception, please refer to this old blog for more information.