Reputation: 23229
I have a Session
object that manages object states (similar to a nhibernate session). This session listens to events from an external source that may require updates to the internal session state. We've tried implementing locking in the session to ensure the data accessed is coherent but there's so many fiddly edge cases.
Instead it might be easier to marshal these events onto the same thread as is using the session (which is the UI thread). Generally this is done with Control.Invoke()
but because this is a data object there is no Control
to access.
Is this a reasonable approach and how can I go about getting this events onto the UI thread before they update the session state? Can I use the Dispatcher
and capture the current thread's dispatcher when the session is created?
Upvotes: 2
Views: 979
Reputation: 244998
I think that locking is usually pretty straight-forward and it may be your best option. Implementing what you want correctly would probably be much more difficult (and certainly not without “fiddly edge cases”).
What you could do though is use existing implementation of BlockingCollection<T>
from .Net 4 and dequeue from it in single thread. Unfortunately, that thread would be blocked, so you can't use the UI thread.
Upvotes: 1
Reputation: 33272
I would let the business object fire an event to be caught by the view ( UI ) and do the marshaling on that event handler, so you have in that place a Control
to understand if an invoke is needed or not:
public static class ControlExtentions
{
public delegate void InvokeHandler();
public static bool SafeInvoke(this Control control, InvokeHandler handler)
{
if (control.InvokeRequired)
{
try
{
control.Invoke(handler);
}
finally { }
return false;
}
else
handler.Invoke();
return true;
}
}
if you are using WPF, you can take inspiration from CaliburnMicro:
public static class Execute
{
private static Action<System.Action> executor = action => action();
/// <summary>
/// Initializes the framework using the current dispatcher.
/// </summary>
public static void InitializeWithDispatcher()
{
#if SILVERLIGHT
var dispatcher = Deployment.Current.Dispatcher;
executor = action => {
if(dispatcher.CheckAccess())
action();
else {
var waitHandle = new ManualResetEvent(false);
Exception exception = null;
dispatcher.BeginInvoke(() => {
try {
action();
}
catch(Exception ex) {
exception = ex;
}
waitHandle.Set();
});
waitHandle.WaitOne();
if(exception != null)
throw new TargetInvocationException("An error occurred while dispatching a call to the UI Thread", exception);
}
};
#else
var dispatcher = Dispatcher.CurrentDispatcher;
executor = action => {
if(dispatcher.CheckAccess())
action();
else dispatcher.Invoke(action);
};
#endif
}
/// <summary>
/// Resets the executor to use a non-dispatcher-based action executor.
/// </summary>
public static void ResetWithoutDispatcher() {
executor = action => action();
}
/// <summary>
/// Executes the action on the UI thread.
/// </summary>
/// <param name="action">The action to execute.</param>
public static void OnUIThread(this System.Action action) {
executor(action);
}
}
Upvotes: 1