Using Dynamic Proxies to Thread Enable MVP WinForms

The original version of the threaded code used two classes to implement the thread safe view and threaded presenter. These classes were specific to IView and IPresenter used by this application.

presenter.View = new ThreadSafeView(form, form);form.Presenter = new ThreadedPresenter(presenter);

The content of this post may seem out of place if you have not read the other posts in the series.

This approach works fine for the simple application presented in the previous post. We only have one view and one presenter. What would happen if we had many views and presenters as is typical in large applications? Our code would explode with many different ThreadedPresenters and ThreadSafeViews. Each of these thread enabled presenters and views will have the same basic implementation. That does not sound very DRY to me. There must be a better way.Dynamic Proxies to the RescueDynamic proxies let us code the basic implementation of both the ThreadedPresenters and ThreadSafeViews in one location and use this implementation for all of our views and presenters. Dynamic proxies allow us to remove the ThreadSafeView and ThreadedPresenter classes and use the factory pattern to create our thread enabled presenter and view.

presenter.View = ThreadHelper.CreateThreadSafeView<IView>(form, form);form.Presenter = ThreadHelper.CreateThreadedPresenter<IPresenter>(presenter);

The threading magic is now located inside of the ThreadHelper class.

public class ThreadHelper{    public static T CreateThreadedPresenter<T>(T adaptee) {        ProxyGenerator generator = new ProxyGenerator();        return (T) generator.CreateProxy(typeof(T), new PresenterInterceptor(), adaptee);    }    public static T CreateThreadSafeView<T>(T adaptee, ISynchronizeInvoke synchronizer) {        ProxyGenerator generator = new ProxyGenerator();        return (T) generator.CreateProxy(typeof(T), new ViewInterceptor(synchronizer), adaptee);    }}

The real magic is in Castle’s ProxyGenerator. ProxyGenerator’s CreateProxy method allows us to create a dynamic proxy for the interface T. PresenterInterceptor and ViewInterceptor incapsulate how we thread enable a presenter or a view.

public class PresenterInterceptor : IInterceptor {    public object Intercept(IInvocation invocation, params object[] args)    {        ThreadPool.QueueUserWorkItem(delegate(object state) {invocation.Proceed(args);});        return null;    }}

The PresenterInterceptor class is trivial. Each method invocation on the presenter interface is placed on the ThreadPool’s queue. The ThreadPool will execute the presenter method when a thread is available. That is it. Nothing else needs to be done.

The above code assumes that all of your Presenter interface methods have a return type of void. What? That is a huge restriction, right? No. Threading requires that you separate asking the question from getting the answer. Methods that have non-void return types cannot be threaded in any real way.

public class ViewInterceptor : IInterceptor {    private static readonly object[] EMPTY_ARGS = new object[0];    private readonly ISynchronizeInvoke synchronizer;    private delegate void Invoker();    public ViewInterceptor(ISynchronizeInvoke synchronizer) {        this.synchronizer = synchronizer;    }    public object Intercept(IInvocation invocation,                            params object[] args)    {        InvocationInvoker invoker = new InvocationInvoker(invocation, args);        synchronizer.BeginInvoke(new Invoker(invoker.Invoke), EMPTY_ARGS);        return null;    }}public class InvocationInvoker {    private IInvocation invocation;    private object[] args;    public InvocationInvoker(IInvocation invocation, object[] args) {        this.invocation = invocation;        this.args = args;    }    public void Invoke() {        invocation.Proceed(args);    }}

The ViewInterceptor is not as easy. It should be, but C# gets in the way. The majority of the code is focused around synchronizer.BeginInvoke The BeginInvoke method ensures the call to the view gets executed on the application’s GUI thread. The rest of the code is noise required by C#.The full source for this post can be found in my subversion repository.https://esterline.devguard.com/svn/code/MultiThreadWinFormsAll questions and/or comments are welcome.

Advertisements

5 Responses

  1. […] a BackgroundWorker to keep the WinForm application responsive during long running operations. In part four, we will use dynamic proxies to keep our code […]

  2. […] us to revisit this application through out the series (Part 2 – BackgroundWorker, Part 3 – MVP and Part 4 – Dynamic Proxies) discussing different ways to inject threads into your WinForms […]

  3. […] Multi-Threaded WinForm Application – BackgroundWorker Welcome back to the second installment of a four part series (Part 1 – Basics, Part 3 – MVP and Part 4 – Dynamic Proxies). […]

  4. Nice article! Exactly what I’m looking for. Thanks.

  5. Hello
    Very Interesting…

    G’night

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: