|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Please see the Download section below for instructions on how to run the code.
PrefaceThis article is a response to the article Weak Events in C# by Daniel Grunwald. Introduction
Features
The subscriber does not have to know the publisher. Both of them only need to know the 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 Publishers and subscribers are referenced by weak references which will not prevent them from being garbage collected – like in the normal event registration model. Only publishers and subscribers that are registered on the same 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. BackgroundThis Using the codeSample publisherPublish 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 Sample subscriberSubscribe 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 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 Publication optionsSimple[EventPublication("Simple")]
public event EventHandler SimpleEvent;
With custom EventargsNote: [EventPublication("CustomEventArgs")]
public event EventHandler<CustomEventArguments> CustomEventArgs;
Publish multiple event topics with one single event[EventPublication("Event1")]
[EventPublication("Event2")]
[EventPublication("Event3")]
public event EventHandler MultiplePublicationTopics;
Allow only synchronous subscription handlersSee the sections Comparison to CAB EventBroker/Subscription handler restrictions for more details. [EventPublication("test", HandlerRestriction.Synchronous)]
public event EventHandler AnEvent;
Allow only asynchronous subscription handlersSee the section Comparison to CAB EventBroker/Subscription handler restrictions for more details. [EventPublication("test", HandlerRestriction.Asynchronous)]
public event EventHandler AnEvent;
Subscription optionsSimple[EventSubscription("Simple", typeof(Handlers.Publisher)]
public void SimpleEvent(object sender, EventArgs e) {}
Custom Eventargs[EventSubscription("CustomEventArgs"), typeof(Handlers.Publisher))]
public void CustomEventArgs(object sender, CustomEventArgs e) {}
Subscribe multiple event topics[EventSubscription("Event1", typeof(Handlers.Publisher))]
[EventSubscription("Event2", typeof(Handlers.Publisher))]
[EventSubscription("Event3", typeof(Handlers.Publisher))]
public void MultipleSubscriptionTopics(object sender, EventArgs e) {}
Execute handler on background thread (asynchronous)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) {}
Execute handler on UI threadUse this option if calling from a background worker thread to a user interface component that updates the user interface - no need for [EventSubscription("UI", typeof(Handlers.UserInterface))]
public void UI(object sender, EventArgs e) {}
Note that if you use the Execute handler on UI thread asynchronouslyThe 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) {}
Fire event topics directly on EventBrokerEvent topics can be fired directly on the 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.Fire("topic", sender, eventArgs);
ScopeSometimes it is necessary to limit the scope an event is published to. This can be achieved in two different ways: Multiple instances of event brokersSubscribers 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. Hierarchical namingPublishers and subscribers can be named by implementing the 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:
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. Comparison to CAB EventBrokerAs stated in the Background section, my This section describes the differences. StandaloneThe Developer guidanceI tried to implement the
Subscription handler restrictionsNote: 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 [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;
LoggingThe Note: ExtensibilityThreadOption --> HandlerI replaced the enum Scope --> Scope MatcherYou can implement your own scope matchers to provide an event hierarchy that suits your needs instead of specifying a scope with an enum. InternalsWe'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. DownloadThe 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. LicenseNote that the bbv.Common.EventBroker is licensed under Apache License 2.0, but because the History
| ||||||||||||||||||||