Push Data Flow Model in C# 4.0

In the pre 4.0 era, there are two interfaces reveal a new world to .net world. The two hero are IEnumerable<T> and IEnumerator<T>.
Based on these two interfaces, .net introduced a lot of amazing staffs that dramatically simplified the development related to data flow, these well known features are: foreach loop, yield keyword, and LINQ.
These 2 interfaces provide an common abstraction layer for the operation that pull data from the data source. And with this abstraction, you can apply foreach loop to almost every kind of data source in the .net world.
IEnumerable<T> and IEnumerator<T> are cool, but they are somehow not so convenient to use in some cases, such as in asynchronous context or in obvious latency environment.
And the pull data flow model also has a capable brother, the push data flow, which can fill up the gap that pull model left for us.

So in .net 4.0, Microsoft introduce another 2 great interfaces, called IObservable<T> and IObserver<T>. These two interfaces, just as IEnumerable<T> and IEnumerator<T>, also open a door to the new world for every .net developer. With these 2 interfaces, people can setup a lot of features that corresponding to IEnumerable<T> and IEnumerator<T>.

Now Microsoft has a great library that called Rx(which stands for “Reactive Extension”), which provided a lot of features that similar to LINQ and more based on IObserable<T> and IObserver<T>.

Sync Notify Pattern for WPF Cross Thread View Model

WPF has a powerful data binding mechanism, by which it is easy to implement MVVM and MVC pattern for UI application.
But thing gets not so beautiful once you introduced multi-thread into the app. Somehow, it means you have to manually call dispatcher method to synchronize the thread context, or else INotifyPropertyChanged contract causes cross thread violation, which is likely to drive UI component throw exception. But tons of calls to Dispatcher.Invoke or Dispatcher.BeginInvoke make you code ugly and hard to maintain. And it is boring to call dispatcher every time you try to write view model properties.

Is there any solution to this embarrassed situation?
The answer is Sync Notify pattern.

By analyzing the WPF data binding data flow, you can find the best point to behave the thread synchronization is at the boundary of your code and WPF implementation, which is the property changed event raiser. To sync the thread context here makes your code clean and effective.

Typically, you might implement the INotifyPropertyChanged in following way:

INofityPropertyChanged Implementation
1
2
3
4
5
6
7
8
9
10
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion

And you might implement the property of view model as following:

Notify Property Decalration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#region Notify Property ViewModelProperty
private string viewModelPropertyBackField;
public string ViewModelProperty
{
get { return viewModelPropertyBackField; }
set
{
if (viewModelPropertyBackField == value)
return;
viewModelPropertyBackField = value;
Notify("ViewModelProperty");
}
}
#endregion

This implementation works perfect in single thread context , but fails in multi-thread context. So we introduce a new event raiser implementation, which synchronize the thread context before raising the event:

SyncNotify
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#region Sync INotifyPropertyChanged Members
protected void SyncNotify(string propertyName, bool wait = false, Dispatcher dispatcher = null)
{
if (PropertyChanged == null)
return;
dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher;
if (dispatcher.Thread == Thread.CurrentThread)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
else
{
if (wait)
{
dispatcher.Invoke(PropertyChanged, this, new PropertyChangedEventArgs(propertyName));
}
else
{
dispatcher.BeginInvoke(PropertyChanged, this, new PropertyChangedEventArgs(propertyName));
}
}
}
#endregion

This method check the whether it is running on the same thread as dispatcher, it not it synchronize the thread. It works perfect in most common cases.
But this implementation requires you to add some new code to the view model, and requires the event works as a delegate. This restriction sometime hurt, such as when you try to derive your view model from ObservableCollection<T>.
ObservableCollection<T> declared PropertyChanged event, but implemented it explicitly, which means you won’t be able to raise the event by calling PropertyChanged directly. Instead, you need to call protected method OnPropertyChanged.

In such cases, you need the following implementation powered by extension method:

SyncNotifyExtension
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
public static class SyncNotifyExtension
{
public static void SyncNotify(this INotifyPropertyChanged host, string propertyName, Action<PropertyChangedEventArgs> eventRaiser, bool wait = false, Dispatcher dispatcher = null)
{
Contract.Requires<ArgumentNullException>(host != null);
Contract.Requires<ArgumentException>(string.IsNullOrWhiteSpace(propertyName));
Contract.Requires<ArgumentNullException>(eventRaiser != null);
dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher;
if (dispatcher.Thread == Thread.CurrentThread)
{
eventRaiser(new PropertyChangedEventArgs(propertyName));
}
else
{
if (wait)
{
dispatcher.Invoke(eventRaiser, new PropertyChangedEventArgs(propertyName));
}
else
{
dispatcher.BeginInvoke(eventRaiser, new PropertyChangedEventArgs(propertyName));
}
}
}
public static void SyncNotify(this INotifyPropertyChanged host, string propertyName, Action<string> eventRaiser, bool wait = false, Dispatcher dispatcher = null)
{
Contract.Requires<ArgumentNullException>(host != null);
Contract.Requires<ArgumentException>(string.IsNullOrWhiteSpace(propertyName));
Contract.Requires<ArgumentNullException>(eventRaiser != null);
dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher;
if (dispatcher.Thread == Thread.CurrentThread)
{
eventRaiser(propertyName);
}
else
{
if (wait)
{
dispatcher.Invoke(eventRaiser, propertyName);
}
else
{
dispatcher.BeginInvoke(eventRaiser, propertyName);
}
}
}
public static void SyncNotify(this INotifyPropertyChanged host, string propertyName, Action<object, PropertyChangedEventArgs> eventRaiser, bool wait = false, Dispatcher dispatcher = null)
{
Contract.Requires<ArgumentNullException>(host != null);
Contract.Requires<ArgumentException>(string.IsNullOrWhiteSpace(propertyName));
Contract.Requires<ArgumentNullException>(eventRaiser != null);
dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher;
if (dispatcher.Thread == Thread.CurrentThread)
{
eventRaiser(host, new PropertyChangedEventArgs(propertyName));
}
else
{
if (wait)
{
dispatcher.Invoke(eventRaiser, host, new PropertyChangedEventArgs(propertyName));
}
else
{
dispatcher.BeginInvoke(eventRaiser, host, new PropertyChangedEventArgs(propertyName));
}
}
}
public static void SyncNotify(this INotifyPropertyChanged host, string propertyName, Action<object, string> eventRaiser, bool wait = false, Dispatcher dispatcher = null)
{
Contract.Requires<ArgumentNullException>(host != null);
Contract.Requires<ArgumentException>(string.IsNullOrWhiteSpace(propertyName));
Contract.Requires<ArgumentNullException>(eventRaiser != null);
dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher;
if (dispatcher.Thread == Thread.CurrentThread)
{
eventRaiser(host, propertyName);
}
else
{
if (wait)
{
dispatcher.Invoke(eventRaiser, host, propertyName);
}
else
{
dispatcher.BeginInvoke(eventRaiser, host, propertyName);
}
}
}
}

These extension methods can be called inside the view model, and it ensures that the event is raised on the dispatcher thread.
Since these extension methods uses delegate to invoke the event raiser, so it is unnecessary to worry about the accessibility of the event raiser, event it is declared as protected or private. And 4 overloads can handle most common event raisers.

The problem introduced by making structure as a property

I met a strange problem while I was evaluating the Xaml Services that introduced in .net 4.
I created 2 types for Xaml Serialization Test:

Model
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Foo
{
public Foo() { }
public Foo(int a)
{
Random RND = new Random();
Value = RND.Next();
MyProperty = RND.Next();
Complex.Init(); // Complex is value type, which is automatically instantiated by default constructor.
}
[Ambient]
public int Value { get; private set; }
[Ambient]
public int MyProperty { get; private set; }
[Ambient]
public Nested Complex;
}
public struct Nested
{
public void Init()
{
this.Temp = Guid.NewGuid();
}
[Ambient]
public Guid Temp { get; set; }
}

So I can create a test instance of Foo by calling Foo(int a), e.g:

Instantiate Foo
1
Foo foo = new Foo(1);

But since Xaml doesn’t support serialize fields, I cannot apply AmbientAttribute to field, so the compilation fails. So I changed the field Complex of Class Foo to a property:

Change Field to Property
1
2
3
[Ambient]
// public Nested Complex;
public Nested Complex { get; set;}

Then the code compiles correct. But when I executed the code, I found the Temp property of Complex is empty guid. But I found I do called the Init method in the constructor of Foo. What’s the problem?

I believe that the problem must be special behavior related to ValueType. So I omitted to call Init(), I try to assign the Complex in Foo’s Constructor as following code

Replace the initializer with assignment
1
2
//Complex.Init();
this.Complex.Temp = Guid.NewGuid();

Compiles fails! Visual Studio tells me “Cannot modify the return value of ‘structTest.Foo.Complex’ because it is not a variable”

Still now, everything is explainable:

  1. It is because this.Complex is pointed to the variable itself that Code Complex.Init() works when Complex is field.
  2. It is because this.Complex is a return value, a copy of the original variable, instead of the original variable that the Code Complex.Init() doesn’t work when Complex is a property. this.Complex.Init() do changed the value of the Temp, but it modified the new copy instead of the original one.

So we need to be careful while change a value type field into a property, which may modify the program behavior a lot, and really difficult to debug while the code is complicated.