![]() |
Platforms, Frameworks & Libraries »
.NET Framework »
General
Advanced
License: The Apache License, Version 2.0
EventBroker: a notification component for synchronous and asynchronous, loosly coupled event handlingBy Urs EnzlerEventBroker is a notification component for (a)synchronous loosly coupled event handling. |
C#2.0, C#3.0.NET2.0, .NET3.0, .NET3.5, Architect, Dev, Design
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
Please see the Download section below for instructions on how to run the code.

This article is a response to the article Weak Events in C# by Daniel Grunwald.
EventBroker is a component that you can use to fire and receive notifications in your system.
The subscriber does not have to know the publisher. Both of them only need to know the EventTopic URI (a string uniquely identifying the EventTopic in the system). This facilitates building loosely coupled systems.
The subscriber defines on which thread the subscription handler is executed:
Publishers can restrict subscriptions to events to be synchronous or asynchronous.
Several publishers/subscribers can fire/handle the same EventTopic.
Publishers and subscribers are referenced by weak references which will not prevent them from being garbage collected – like in the normal event registration model.
EventBroker:Only publishers and subscribers that are registered on the same EventBroker are wired together when events are fired. Normally, you'll use a single EventBroker in your system that handles all event notifications. However, in special cases, it is useful to define a new EventBroker for a sub system. This gives you the possibility to define a scope for your event notification.
Publishers and subscribers can be named in a hierarchical way, and events can be global, to parents only, or to children only. Both publishers and subscribers can define the scope they publish to/receive from.
This EventBroker is based upon the EventBroker from the Composite (UI) Application Block from Microsoft. See the section Comparison to CAB EventBroker below, for differences.
Publish an event topic:
public class Publisher
{
[EventPublication("topic://EventBrokerSample/SimpleEvent")]
public event EventHandler SimpleEvent;
///<summary>Fires the SimpleEvent</summary>
public void CallSimpleEvent()
{
SimpleEvent(this, EventArgs.Empty);
}
}
Register the publisher with your event broker (you have to hold an instance of the event broker somewhere in your code). The sample assumes there is a service that holds the event broker instance for us:
EventBroker eb = Service.EventBroker;
Publisher p = new Publisher();
eb.Register(p);
On registration of the publisher, the event broker inspects the publisher for published events (events with the EventPublication attribute).
Subscribe to an event topic:
public class Subscriber
{
[EventSubscription(
"topic://EventBrokerSample/SimpleEvent",
typeof(Handlers.Publisher))]
public void SimpleEvent(object sender, EventArgs e)
{
// do something useful or at least funny
}
}
Register the subscriber with the event broker:
EventBroker eb = Service.EventBroker;
Subscriber s = new Subscriber();
eb.Register(s);
The event broker will inspect the subscriber, on registration, for subscription to event topics (methods with the EventSubscription attribute).
If a publisher fires an event topic for that subscribers are registered, then the event broker will relay them to the subscribers by calling the subscription handler methods with the sender and Eventargs the publisher used to fire its event.
[EventPublication("Simple")]
public event EventHandler SimpleEvent;
Note: CustomEventArgs has simply to be derived from EventArgs.
[EventPublication("CustomEventArgs")]
public event EventHandler<CustomEventArguments> CustomEventArgs;
[EventPublication("Event1")]
[EventPublication("Event2")]
[EventPublication("Event3")]
public event EventHandler MultiplePublicationTopics;
See the sections Comparison to CAB EventBroker/Subscription handler restrictions for more details.
[EventPublication("test", HandlerRestriction.Synchronous)]
public event EventHandler AnEvent;
See the section Comparison to CAB EventBroker/Subscription handler restrictions for more details.
[EventPublication("test", HandlerRestriction.Asynchronous)]
public event EventHandler AnEvent;
[EventSubscription("Simple", typeof(Handlers.Publisher)]
public void SimpleEvent(object sender, EventArgs e) {}
[EventSubscription("CustomEventArgs"), typeof(Handlers.Publisher))]
public void CustomEventArgs(object sender, CustomEventArgs e) {}
[EventSubscription("Event1", typeof(Handlers.Publisher))]
[EventSubscription("Event2", typeof(Handlers.Publisher))]
[EventSubscription("Event3", typeof(Handlers.Publisher))]
public void MultipleSubscriptionTopics(object sender, EventArgs e) {}
The event broker creates a worker thread to execute the handler method on. The publisher can immediately continue processing.
[EventSubscription("Background", typeof(Handlers.Background))]
public void BackgroundThread(object sender, EventArgs e) {}
Use this option if calling from a background worker thread to a user interface component that updates the user interface - no need for Control.Invoke(...) anymore.
[EventSubscription("UI", typeof(Handlers.UserInterface))]
public void UI(object sender, EventArgs e) {}
Note that if you use the UserInterface handler, then you have to make sure that you register the subscriber on the user interface thread. Otherwise, the EventBroker won't be able to switch to the user interface thread, and will throw an exception.
The same as above, but the publisher is not blocked until the subscriber has processed the event.
[EventSubscription("UIAsync", typeof(Handlers.UserInterfaceAsync))]
public void UI(object sender, EventArgs e) {}
Event topics can be fired directly on the EventBroker without the need of registering a publisher. This comes in handy whenever you need to fire an event topic from an object that lives only very shortly.
A good example for such a scenario is a scheduled job execution. At the scheduled time, an instance of a job class is instantiated and executed. It would be cumbersome to register this instance, fire the event, and unregister it again - it's far easier to fire the event directly on the EventBroker:
eventBroker.Fire("topic", sender, eventArgs);
Sometimes it is necessary to limit the scope an event is published to. This can be achieved in two different ways:
Subscribers can only listen to events of publishers that are registered on the same event broker. Therefore, an event broker automatically builds a scope.
This is the easiest solution to have several event handling scopes in your application, and should always be preferred. However, sometimes, you need more control over scopes within the objects on one single event broker. This scenario is described in the following section.
Publishers and subscribers can be named by implementing the INamedItem interface. This interface provides a single property:
string EventBrokerItemName { get; }
This allows identifying an object within the event broker whereas the publication and subscription attributes are always bound to the class.
The naming is hierarchical by using the same scheme as namespaces:
Test is a parent of Test.MyPublisher1Test.MyName1 is a sibling of Test.MyName2Test.MyName1.Subname is a child of Test.MyName1Test.MyName1 is a twin of Test.MyName1 (two objects with the same name are twins)Now, a publisher can define the scope it wants to publish the event to, by defining a scope in the publication attribute:
[EventPublication("Topic")]
public event EventHandler PublishedGlobally;
[EventPublication("Topic", typeof(ScopeMatchers.PublishToParents)]
public event EventHandler PublishedToParentsAndTwinsOnly;
[EventPublication("Topic", typeof(ScopeMatchers.PublishToChildren)]
public event EventHandler PublishedToChildrenAndTwinsOnly;
The first event is a global event that all subscribers can receive. The second event is only passed to subscribers that are parents or twins of the publisher. The third event is only passed to subscribers that are children or twins of the publisher.
A subscriber can define the scope it wants to receive events from accordingly:
[EventSubscription("Topic", typeof(Handlers.Publisher)]
public void GlobalHandler(object sender, EventArgs e)
[EventSubscription(
"Topic",
typeof(Handlers.Publisher),
typeof(ScopeMatchers.SubscribeToParents)]
public void ParentHandler(object sender, EventArgs e)
[EventSubscription(
"Topic",
typeof(Handlers.Publisher),
typeof(ScopeMatchers.SubscribeToChildren)]
public void ChildrenHandler(object sender, EventArgs e)
The first subscription is a global subscription that will handle all the events passed to it. The second subscription will only be called when a parent of the subscriber is firing the event. The third subscription will only be called when a child of the subscriber is firing the event.
Twins (different objects with the same name) are handled specially. A twin is always a parent and child of its twin, and will therefore will always receive all the events from its twin.
As stated in the Background section, my EventBroker is based on the EventBroker from CAB (Composite UI Application Block) from the Practices and Patterns group of Microsoft.
This section describes the differences.
The bbv.Common EventBroker can be used standalone. You can use it anywhere in your projects you need notification, without any framework constraints as CAB would require.
I tried to implement the EventBroker in a way that errors get visible as soon as possible. I will give you some examples of what that means:
EventHandler the published event provides does not match the signature the subscription handler provides, then an exception is thrown at registration time, instead of when the event is fired.UserInterface or UserInterfaceAsync subscription handlers, then an exception is thrown at registration time if no WindowsFormsSynchronizationContext is present, that means the current thread is not the user interface thread. This would lead to cross threading exceptions when handling events.Note: this feature is only available in the version hosted on Sourceforge.net (see the section Download) and not in the attached solution (it's too new ;-) ).
We had the problem that some publisher has to be sure that all subscribers handle its event synchronously. For example, if you publish a cancel event, providing a way for all subscribers to cancel the current operation. Only if no subscriber sets the Cancel property on the CancelEventArgs to true can the publisher proceed with its operation. Therefore, the publisher has to restrict all subscriptions on this event to be synchronous:
[EventPublication("test", HandlerRestriction.Synchronous)]
public event EventHandler AnEvent;
If a subscriber registers an asynchronous handler for this event, then an exception is thrown. Note that the exception is thrown at registration time, and not when the event is fired. This simplifies writing consistent code a lot.
Furthermore, a publisher can restrict subscription handlers to be asynchronous because the publisher does not want to be blocked:
[EventPublication("test", HandlerRestriction.Asynchronous)]
public event EventHandler AnEvent;
The EventBroker of bbv.Common is quite chatty in terms of log messages. This enables you to see when a publisher fires an event, how they are routed to subscribers, how they are handled, and which exceptions occurred.
Note: bbv.Common uses log4net to log messages. That means, you can configure the level of messages logged for each component.
I replaced the enum ThreadOption with extensible handlers to define the way the event is handled (synchronously, asynchronously).
You can implement your own scope matchers to provide an event hierarchy that suits your needs instead of specifying a scope with an enum.
We'll have a look at the internals on a next update of this article. Until then, please refer to the source code available in the download.
The download at the top of this article contains three Visual Studio 2008 projects:
The bbv.Common.EventBroker is part of a larger library containing some other cool components, which can be found here.
This download is just a stripped down version. To try it out, open the solution, set the start up project to bbv.Common.EventBroker.Sample, and hit F5.
Note that the version attached to this article is not the latest available. Be sure to check the Sourceforge page for the latest release.
Note that the bbv.Common.EventBroker is licensed under Apache License 2.0, but because the EventBroker contains parts of the CAB from Microsoft, additional license restrictions have to be applied, see file headers in the source code for details.
EventBroker, added link to Sourceforge.net.
General
News
Question
Answer
Joke
Rant
Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads.
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 26 Oct 2008 Editor: Smitha Vijayan |
Copyright 2008 by Urs Enzler Everything else Copyright © CodeProject, 1999-2010 Web21 | Advertise on the Code Project |