Real application scenario of using TPL Dataflow in MVVM WPF application
-
Wednesday, October 05, 2011 10:58 AM
May I ask you to point me to some example showing how to use TPL Dataflow in MVVM application when TPL Dataflow is used in a data service?
In my MVVM application I separated application blocks to 5 different projects implementing model (domain and services projects responsible for updating model), view model (applications project) and view (presentation project - WPF application).
When testing domain, services and applications block using a console application all works fine.
For now I created Winform project to test what I will put later in WPF application, to test applications block, but nothing works.
I looked samples in Async CTP: (C# Dataflow) Real Estate, (C# Dataflow) Dining Philosophers. Well those examples shows how to use Dataflow but the code is put in the GUI, not separated in some service assembly.
My domain, and applications block are quite complex and I do not want to put them in GUI.
I read about TPL Dataflow as a successor for CCR and as matter of fact I already used CCR in my project couple years ago. The CCR worked for me in such scenario.
My naive understanding of TPL Dataflow was that it is similar like CCR where you setup your ports and activate arbiters, then any job/task you post to a port is executed by a scheduler on a thread, as I mention such scenario works for me.
In TPL Dataflow you have got ActionBlock like a port in CCR, so again posting a job/task to ActionBlock or to some BufferBlock linked to (LinkTo) an action block would execute your task.
In my console application all works fine. Any job posted to action block is executed.
When I use my service assembly from winforms application no task is executed. In my test I simulate one job execution (login to asmx web services using WCF client, where communication is done by async/await) and as well repeating task execution (my services queries asmx web service for market prices.) The job responsible for a market price retrieval contains await TaskEx.Delay(1000), so when market prices are updated job execution is postponed for 1 second, and if market flag update is active, my service repost this job again for execution. This way there is no loop for market prices monitoring, but a loop is done by reposting the same job.
My problem is maybe that I do not understand how TPL Dataflow executes tasks, I thought that when action block is initialized a task scheduler is created for an action block, so some thread is created for this action block, when the action block receives a task then it is scheduled on the same thread, or new thread is created in thread pool.
What I want to achieve is that my service should run on other thread than UI one. The service will execute my application logic, updates my model, and when model data are changed, the changes will be propagated to my UI.
- Edited by StefanBelo Wednesday, October 05, 2011 3:51 PM update
- Edited by StefanBelo Wednesday, October 05, 2011 3:52 PM update
- Edited by StefanBelo Wednesday, October 05, 2011 3:53 PM
All Replies
-
Thursday, October 06, 2011 7:21 AM
English is foreign language for me, so maybe what I tried to explain does not make any sense for you. Here there is a video showing my test.
My question is why this simple TPL dataflow code fails to run in Windows Forms application?
-
Friday, October 07, 2011 7:13 PM
Hi Stefan,
I suspect your problem is similar to the one described in this thread - http://social.msdn.microsoft.com/Forums/en-US/tpldataflow/thread/3a8b1e87-3c39-437d-af14-7511adee1ca3. Please read Steve Toub’s answer.
If you want to have a greater control over the scheduling, pass in the desired TaskScheduler through the DataflowBlockOptions and use a normal (not async) delegate.
Zlatko Michailov
Software Development Engineer, Parallel Computing Platform
Microsoft Corp.
This posting is provided "AS IS" with no warranties, and confers no rights. -
Saturday, October 08, 2011 2:01 PM
Hi Zlatko
I removed async delegate for action block but my code behaves exactly the same way.
The code works in a console app, but not in Winforms or WPF app. It throws the following exceptions deep in TPL and Dataflow assemblies:
A first chance exception of type 'System.InvalidOperationException' occurred in System.ServiceModel.dll
A first chance exception of type 'System.InvalidOperationException' occurred in AsyncCtpLibrary.dll
A first chance exception of type 'System.AggregateException' occurred in mscorlib.dll
A first chance exception of type 'System.AggregateException' occurred in System.Threading.Tasks.Dataflow.dll
-
Monday, October 10, 2011 4:14 PM
Hi Stefan,
The purpose of using a non-async delegate is to make the TaskScheduler option that you pass in to the ActionBlock relevant. In particular, you want to pass in TaskScheduler.Current. I still don’t see that in your code.
Zlatko Michailov
Software Development Engineer, Parallel Computing Platform
Microsoft Corp.
This posting is provided "AS IS" with no warranties, and confers no rights. -
Monday, October 10, 2011 4:30 PM
Zlatko, it does not work:
var ce = new ConcurrentExclusiveSchedulerPair(); var exclusive = new ExecutionDataflowBlockOptions { TaskScheduler = ce.ExclusiveScheduler }; var concurrent = new ExecutionDataflowBlockOptions { TaskScheduler = ce.ConcurrentScheduler, MaxDegreeOfParallelism = 20 }; executeJob = new ActionBlock(job => { Task jobTask = job.Execute(); if (job.HaveJobDoneAction) { job.ExecuteJobDone(jobTask.Result); } }, concurrent ); -
Monday, October 10, 2011 5:12 PM
I’ve given you some wrong advice – TaskScheduler.Current won’t force scheduling on the UI thread in general. Sorry about that.
The good news is you weren’t following my advice anyway. I don’t know how you decided to use ConcurrentExclusiveTaskSchedulerPair, but that still needs an explicit underlying scheduler. Otherwise it would pick TaskScheduler.Default.
To force scheduling on the UI thread, you need to call TaskScheduler.FromCurrentSynchronizationContext() (http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.fromcurrentsynchronizationcontext.aspx) on the UI thread, save the result, and pass it in as a TaskScheduler option wherever you create Dataflow blocks or TPL tasks that have to execute on the UI thread.
Here is an example that shows that - http://msdn.microsoft.com/en-us/library/dd997394.aspx.
Zlatko Michailov
Software Development Engineer, Parallel Computing Platform
Microsoft Corp.
This posting is provided "AS IS" with no warranties, and confers no rights. -
Tuesday, October 11, 2011 10:58 AM
Zlatko, we do not understand each other.
I have prepared simple project you can run and hopefully answer my question. Change refrences to TPL and TPL Dataflow assemblies.
First run Test project, it is console app and all will work. Then switch to WPF or Winforms test project and run it. (Click on Login button).
Please download the test project from this url.
-
Tuesday, October 11, 2011 5:39 PM
Hi Stefan,
I see you have applied the changes I recommended to you yesterday, and now your Dataflow code works. (I tested the TestWinForms project.)
What doesn’t work is your job execution logic. (This is all in XoTraderService.cs.) In particular, the fact that that your service fires the OnStarted event on arbitrary threads:
public void Login(string userName, string password)
{
ExecuteJob(
new JobInitializeXoTraderService(userName, password,
(status, job) =>
{
if (status)
{
JobInitializeXoTraderService myJob = (JobInitializeXoTraderService)job;
betfairWebServicePool = myJob.BetfairWebServicePool;
}
IsRunning = status;
if (status)
{
//OnStarted(status);
}
}
)
);
}
My advice is that XoTraderService should keep a reference to the synchronization context on which this event should be fired, but you are free to devise your own solution. This is beyond the scope of TPL Dataflow anyway.
To verify your Dataflow code works with that line commented out, you can add these diagnostics:
public XoTraderService()
{
var ce = new ConcurrentExclusiveSchedulerPair();
var exclusive = new ExecutionDataflowBlockOptions { TaskScheduler = ce.ExclusiveScheduler };
var concurrent = new ExecutionDataflowBlockOptions { TaskScheduler = ce.ConcurrentScheduler, MaxDegreeOfParallelism = 20 };
executeJob = new ActionBlock<Job>(job =>
{
Task<bool> jobTask = job.Execute();
if (job.HaveJobDoneAction)
{
job.ExecuteJobDone(jobTask.Result);
}
UpdatedMarket.Post(new Market(42, "BAR (through executeJob)"));
},
concurrent
);
UpdatedMarket = new BroadcastBlock<Market>(null);
UpdatedMarket.Post(new Market(41, "FOO (through UpdatedMarket)"));
}
When you run the TestWinForms project, you’ll see “FOO” before you hit Login. Then you’ll see “BAR” after you hit Login.
Zlatko Michailov
Software Development Engineer, Parallel Computing Platform
Microsoft Corp.
This posting is provided "AS IS" with no warranties, and confers no rights.- Proposed As Answer by Zlatko Michailov - MSFT Tuesday, October 11, 2011 6:17 PM
- Marked As Answer by StefanBelo Thursday, October 13, 2011 11:48 AM
-
Thursday, October 13, 2011 11:48 AMThanks Zlatko. Does it mean that we should prefer BroadcastBlock in such scenario, no events?
-
Thursday, October 13, 2011 5:14 PM
Events are simple to subscribe to and to fire when you are on the UI thread. When you are not on the UI thread, that simplicity will vanish. On the other hand, if you want to use a BroadcastBlock to fire events, you need another Dataflow block, e.g. ActionBlock, to receive it. You have to explicitly provide task schedulers, but you have full control over how to handle the event correctly. It’s a trade-off you have to make consciously.
Zlatko Michailov
Software Development Engineer, Parallel Computing Platform
Microsoft Corp.
This posting is provided "AS IS" with no warranties, and confers no rights.

