A Mediator Prototype for WPF Apps

UPDATE: For a fully featured and tested Mediator implementation, check out the Messenger class in my MVVM Foundation library.

This blog post shows a prototype implementation of the Mediator design pattern, intended to be used in WPF and, possibly, Silverlight applications.  The Mediator class can be used to provide loosely-coupled message-based communication between various entities, such as ViewModel objects.  The prototype was inspired by this thread on the WPF Disciples group, and particularly by conversations I had with Marlon Grech.  I am posting this prototype on my blog, in hopes of receiving suggestions and new ideas on how to improve the design.
The code reviewed in this blog post works, but is not complete or intended to be used in production applications.

Creating a class that allows various objects to send messages to each other is trivial.  It becomes more challenging when you try to create a reusable class like that which does not leak memory.  Since the Mediator object can live indefinitely, but the objects registered to receive messages might have a shorter lifespan, this could easily become a hotspot for memory leaks to occur.  In an ideal world, any object that registers itself to receive messages should unregister itself when it no longer needs to receive messages.  However, in practice, it’s not always easy to know when an object should unregister itself.  The design of the Mediator class needs to take this into consideration.

Let’s take a quick look at how this Mediator class might be used.  Here’s some code showing how an object registers itself to receive a certain message…

registration

Notice that registering for a message requires you to pass in two parameters.  The first parameter is a unique identifier for that message, in this case a string exposed by the MediatorMessages class.  The second parameter is a delegate of type Action<object>.  In the example above I use a lambda expression to create the delegate, but you could just as easily create a new Action<object> instance, if you’d prefer.

Here is an example of an object sending its colleagues a message:

notification

So far, so good, so it seems.  However, there is a problem.  A delegate, such as Action<object>, stores a reference to the object on which the target method should be invoked.  When you pass that delegate to the Mediator, it stores the delegate in a collection, for later use. If you never remove the delegate from the Mediator, via unregistering (which is something I have yet to implement in the prototype), the registered object will never be eligible for garbage collection since there will be a hard reference to it.  Hence, the memory leak issue I mentioned earlier.

My first thought was to store a reference to the Action<object> in a WeakReference.  Since the Garbage Collector will throw away objects that are referenced only by WeakReference objects, it would seem that this would do the trick.  Unfortunately, it’s not that simple.  The problem with that approach is that the GC will throw away the Action<object> instance because it is only being referenced by a WeakReference!

Marlon came up with a great idea, which borrows Greg Schechter’s idea of subclassing WeakReference to make a “weak delegate.”  This is essentially a delegate that uses a WeakReference to store a reference to the target object, instead of using a normal object reference, as normal delegates do.  The drawback to Marlon’s great idea is that it relies on the use of reflection to invoke the target method on the colleague.  This means that it will have a performance decrease, and won’t run in partial trust.

I took Marlon’s idea, and refined it so that it does not require the use of reflection (at least, not directly).  My implementation uses a class I called WeakAction, which looks like this:

weakaction

The collection used by Mediator, which maps a Message to a list of Action<object>, is called MessageToActionsMap.  That class uses the WeakAction type, as seen below:

message2actions

As you can see in the following Mediator method, this implementation detail does not bubble up into the public-facing API:

mediator

You can download the prototype source code and demo app here: Mediator Prototype and Demo .  NOTE: Be sure to change the file extension from .DOC to .ZIP, and then decompress it.

If you have any suggested improvements, bugs, etc. please don’t hesitate to let me know.

About these ads

35 Responses to A Mediator Prototype for WPF Apps

  1. Jacquers says:

    Good stuff Josh!

    How does this compare to Prism and the Event Aggregation it uses to allow communication between modules?

  2. sacha says:

    I like i, but does Marlon have a point about this not being ok in Partial trust?

  3. Laurent says:

    Hi Josh,

    One thing I am not sure of after reading the article: With this implementation, is it still necessary to offer a way to unregister a “message client” from the mediator? Also, what happens if a client was collected without unregistering, and the action it registered is “hydrated” and called, do we get an exception?

    I guess I should just try it myself :)

    Thanks,
    Laurent

  4. Marlon Grech says:

    Laurent since it is using a WeakRefence this will never happen…. The IsAlive would be false so the Mediator unregisters by the VM automatically

  5. cornerwelt says:

    Information on how to convert file extensions and their counterpart apps which can open them can be found at http://www.file-extension.com/files/ZIP/

  6. Josh Smith says:

    @Jacquers – This is the same idea as an event broker, just a different implementation.

    @sacha – This solution doesn’t work in partial trust, unless your app has ReflectionPermission. I’m fine with that. According to the info I found on the Web, using MethodInfo.Invoke() is supposed to be slower than Delegate.CreateDelegate() + invoking the delegate. Assuming that is correct, this implementation should be faster than Marlon’s (though heavily based on it).

    @Laurent- I agree with Marlon, it isn’t a problem.

  7. I’d definitely take a close look at Prism’s EventAggregator. It solves the same types of problems but offers a more type safety.

  8. Josh Smith says:

    Thanks Rob, I’ll check it out soon.

  9. Jeremy Gray says:

    I’d have to check some IL to be sure, but won’t your WeakAction risk early collection if used with lambdas?

  10. Josh Smith says:

    Jeremy,

    I don’t see what you mean. A lambda is just another way to create a delegate. How does that affect the WeakAction?

    Thanks,
    Josh

  11. Jeremy Gray says:

    The lambda results in the instantiation of a generated class to which no other object would be holding a hard reference, no?

  12. Jeremy Gray says:

    (posted too quickly)

    Would that not result in the action’s target only being held weakly by the WeakAction and strongly by nothing?

  13. Josh Smith says:

    Yes, that’s exactly what it does. The Mediator does not hold strong references to the colleagues that are registered with it. If a colleague is not strongly referenced by anyone else, the Mediator needs to “let go” of it and allow it to be GC’d. See what I mean?

  14. I’d like to add another vote to looking at the Prism EventAggregator and add NServiceBus to the mix.

    EventAggregator which I’ve found useful personally is like a mini NServiceBus. There are many implementations of a in-proc Message Bus components that you could definitely leverage.

  15. Josh Smith says:

    Oh, I see what you’re saying now Jeremy. The delegate created by a lambda expression has its Target set to the object which contains the lambda. If there is some compiler-generated class involved, the delegate doesn’t seem to know about it. Perhaps I’m missing something? Can you give me an example of a lambda expression that would cause the WeakAction to not work properly? Thanks!

  16. Josh Smith says:

    I’ll be sure to check that out, Ryan. Thanks for the pointer.

  17. Jeremy Gray says:

    Hmm, I think I’ll have to try to rephrase. :)

    I do understand exactly what the Mediator is trying to achieve here, but what I am suggesting (and, again, I’d have to take a look at the resulting IL to be certain) is that if a lambda were used for subscription, only the mediator’s WeakAction would hold a reference to the target (which would be the lambda, not the intended subscriber) after the point of subscription, and even if the actual intended subscriber were still alive and reachable the collector could run off and collect the action. The subscriber would be alive and hoping for its action to be invoked long after the reference scheme has resulted in the action’s collection.

    (Apologies if I’m failing to find a way to express this more clearly.)

  18. Josh Smith says:

    Thanks for clarifying, Jeremy. I figured out what you were mentioning and replied (see above). From what I can tell, the object that contains the lambda is always the Target of the resultant Action[object] instance. I don’t know what scenario would create a different Target. Thanks again. :)

  19. Jeremy Gray says:

    Following from the above, I suspect you would need to hold a weak reference to the subscriber, hold a hard reference to the action, extend the action to take the subscriber reference as an argument, and then instruct users of the mediator to be careful with what they capture in their action, as it will be held via a hard reference until the (weakly-referenced) subscriber is collected or otherwise unsubscribed.

    This is the only approach that comes to mind at the moment that can correctly handle both the lambda and non-lambda cases, though it does require increased cooperation on the part of the subscriber.

  20. Jeremy Gray says:

    Ahh, thanks for that tidbit regarding the object that contains the lambda being referred to as the target. That would make it all work nicely.

    Good thing I disclaimed that I would have to check the IL to be sure. ;)

  21. Josh Smith says:

    No worries, it’s all good. This is some obscure stuff. :)

  22. Jeremy Gray says:

    If you are going to look at the Prism EventAggregator, I might also suggest taking a look at Mike Rettig’s Retlang. It has a somewhat different purpose, of course, but is definitely interesting for perspective, compare/contrast, what have you.

  23. Jeremy Gray says:

    In any case, were partial trust to become an issue the approach I mentioned would be an available solution, so it is not as if it was wasted time and effort in the grand scheme of things. :D

  24. pmont says:

    another vote for checking out the Prism EventAggregator.

    Their implementation works, covers expandability, threading, weak or strong references, filtering ( lambda expressions ) and it should be usable outside of prism. ( I have not done this yet, but all of the source is available )

  25. Jeremy Gray says:

    @pmont – Sounds nice! I admit I have not yet had a chance to check it out, but I definitely will when next I’m in the position of integrating one / cooking one up. Thanks for posting up with those details.

  26. pmont says:

    Josh,

    I saw on the google groups that you looked at the Event Aggregator in prism. ( yes there are those of us who are not in the in crowd that read it. ) Glad to hear that you took the time. It is a good starting point IMO.

    Thanks for taking the time to look at it.
    pmont

  27. judahgabriel says:

    Hey Josh.

    Is there a race condition in the CreateAction() method? It checks if the weak reference is alive (which could return true), then proceeds to create a delegate to the target. However, between these 2 actions, the GC may have collected the target.

    I think the proper way may be to do something like this:

    var target = this.Target;
    if(target != null)
    {
    // Now we have a hard reference to the target, preventing the GC from collecting it.
    }

  28. Josh Smith says:

    Great catch, Judah! Thanks for pointing that out. I’ll be sure to include your fix in the next version. :D

  29. John "Z-Bo" Zabroski says:

    I feel this is pretty nasty and an attempt to do copy-and-paste design patterns.

  30. mariocatch says:

    Nice work, Josh!

  31. […] that is more memory safe than my orginal version. You can read about Joshs version at his blog post. The wpf disciples have also been discussing the possibilities of released a best practice set of […]

  32. Alex says:

    Panel 3d question.
    Hi, I having problem to make panel 3d work when nested inside itemscontrol? Node3 and Node4 doesn’t shown.
    Thanks for help!

  33. Alex says:

    |ItemsControl
    |
    |ItemsControl|
    |ItemsControl.ItemsPanel|
    |ItemsPanelTemplate|
    |pnl3D:Panel3D/|
    |/ItemsPanelTemplate|
    |/ItemsControl.ItemsPanel|
    |TextBlock Text=”Node3″/|
    |TextBlock Text=”Node4″/|
    |/ItemsControl|
    |TextBlock Text=”Node1″/|
    |TextBlock Text=”Node2″/|
    |/ItemsControl|

  34. […] have been talking a lot about the Mediator pattern for MVVM applications. Me and Josh Smith revised my previous Mediator implementation and found some problems with […]

Follow

Get every new post delivered to your Inbox.

Join 290 other followers

%d bloggers like this: