Event Aggregation - An Implementation of Observer Pattern

Event Aggregation

The Prism Library provides an event mechanism that enables communications between loosely coupled components in
the application. This mechanism, based on the event aggregator service, allows publishers and subscribers to
communicate through events and still do not have a direct reference to each other.

The EventAggregator provides multicast publish/subscribe functionality. This means there can be multiple publishers
that raise the same event and there can be multiple subscribers listening to the same event. Consider using the
EventAggregator to publish an event across modules and when sending a message between business logic code,
such as controllers and presenters.

Events created with the Prism Library are typed events. This means you can take advantage of compile-time type
checking to detect errors before you run the application. In the Prism Library, the EventAggregator allows
subscribers or publishers to locate a specific EventBase. The event aggregator also allows for multiple publishers and
multiple subscribers, as shown in the following illustration.

Event aggregator

Note:

About .NET Framework Events
Using .NET Framework events is the most simple and straightforward approach for communication between
components if loose coupling is not a requirement. Events in the .NET Framework implement the Publish-Subscribe
pattern, but to subscribe to an object, you need a direct reference to that object, which, in composite
applications, typically resides in another module. This results in a tightly coupled design. Therefore, .NET Framework
events are used for communication within modules instead of between modules.
If you use .NET Framework events, you have to be very careful of memory leaks, especially if you have a non-static
or short-lived component that subscribes to an event on a static or longer-lived one. If you do not unsubscribe the
subscriber, it will be kept alive by the publisher, and this will prevent the first one from being garbage-collected.

IEventAggregator

The EventAggregator class is offered as a service in the container and can be retrieved through the
IEventAggregator interface. The event aggregator is responsible for locating or building events and for keeping a
collection of the events in the system.

public interface IEventAggregator
{
    TEventType GetEvent<TEventType>() where TEventType : EventBase;
}

The EventAggregator constructs the event on its first access if it has not already been constructed. This relieves
the publisher or subscriber from needing to determine whether the event is available.

CompositePresentationEvent

The real work of connecting publishers and subscribers is done by the CompositePresentationEvent class. This is
the only implementation of the EventBase class that is included in the Prism Library. This class maintains the list of
subscribers and handles event dispatching to the subscribers.

The CompositePresentationEvent class is a generic class that requires the payload type to be defined as the
generic type. This helps enforce, at compile time, that publishers and subscribers provide the correct methods for
successful event connection. The following code shows a partial definition of the CompositePresentationEvent
class.

public class CompositePresentationEvent<TPayload> : EventBase
{
    ...
    public SubscriptionToken Subscribe(Action<TPayload> action);
    public SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption threadOption);
    public SubscriptionToken Subscribe(Action<TPayload> action, bool
keepSubscriberReferenceAlive)
    public virtual SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption
threadOption, bool keepSubscriberReferenceAlive);
    public virtual SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption
threadOption, bool keepSubscriberReferenceAlive, Predicate<TPayload> filter);
    public virtual void Publish(TPayload payload);
    public virtual void Unsubscribe(Action<TPayload> subscriber);
    public virtual bool Contains(Action<TPayload> subscriber)
    ...
}

Creating and Publishing Events

The following sections describe how to create, publish, and subscribe to CompositePresentationEvent using the
IEventAggregator interface.

Creating an Event

The CompositePresentationEvent<TPayload> is intended to be the base class for an application's or module's
specific events. TPayLoad is the type of the event's payload. The payload is the argument that will be passed to
subscribers when the event is published.

For example, the following code shows the TickerSymbolSelectedEvent in the Stock Trader Reference
Implementation (Stock Trader RI). The payload is a string containing the company symbol. Notice how the
implementation for this class is empty.

public class TickerSymbolSelectedEvent : CompositePresentationEvent<string>{}

Note:

In a composite application, the events are frequently shared between multiple modules, so they are defined
in a common place. In the Stock Trader RI, this is done in the StockTraderRI.Infrastructure project.

Publishing an Event

Publishers raise an event by retrieving the event from the EventAggregator and calling the Publish method. To
access the EventAggregator, you can use dependency injection by adding a parameter of type IEventAggregator
to the class constructor.

For example, the following code demonstrates publishing the TickerSymbolSelectedEvent.

this.eventAggregator.GetEvent<TickerSymbolSelectedEvent>().Publish("STOCK0");

Subscribing to Events

Subscribers can enlist with an event using one of the Subscribe method overloads available on the
CompositePresentationEvent class. There are several ways to subscribe to CompositePresentationEvents. Use
the following criteria to help determine which option best suits your needs:

  1. If you need to be able to update UI elements when an event is received, subscribe to receive the event on the UI thread.
  2. If you need to filter an event, provide a filter delegate when subscribing.
  3. If you have performance concerns with events, consider using strongly referenced delegates when subscribing and then manually unsubscribe from the CompositePresentationEvent.
  4. If none of the preceding is applicable, use a default subscription.

The following sections describe these options.

Subscribing on the UI Thread

Frequently, subscribers will need to update UI elements in response to events. In WPF and Silverlight, only a UI thread
can update UI elements.

By default, the subscriber receives the event on the publisher's thread. If the publisher sends the event from the UI
thread, the subscriber can update the UI. However, if the publisher's thread is a background thread, the subscriber
may be unable to directly update UI elements. In this case, the subscriber would need to schedule the updates on the
UI thread using the Dispatcher class.

The CompositePresentationEvent provided with the Prism Library can assist by allowing the subscriber to
automatically receive the event on the UI thread. The subscriber indicates this during subscription, as shown in the
following code example.

public void Run()
{
    ...
    this.eventAggregator.GetEvent<TickerSymbolSelectedEvent>().Subscribe(ShowNews,
ThreadOption.UIThread);
);
}
public void ShowNews(string companySymbol)
{
    this.articlePresentationModel.SetTickerSymbol(companySymbol);
}

The following options are available for ThreadOption:

  1. PublisherThread. Use this setting to receive the event on the publishers' thread. This is the default setting.
  2. BackgroundThread. Use this setting to asynchronously receive the event on a .NET Framework thread-pool thread.
  3. UIThread. Use this setting to receive the event on the UI thread.

Subscription Filtering

Subscribers may not need to handle every instance of a published event. In these cases, the subscriber can use the
filter parameter. The filter parameter is of type System.Predicate<TPayLoad> and is a delegate that gets
executed when the event is published to determine if the payload of the published event matches a set of criteria
required to have the subscriber callback invoked. If the payload does not meet the specified criteria, the subscriber
callback is not executed.

Frequently, this filter is supplied as a lambda expression, as shown in the following code example.

FundAddedEvent fundAddedEvent = this.eventAggregator.GetEvent<FundAddedEvent>();

fundAddedEvent.Subscribe(FundAddedEventHandler, ThreadOption.UIThread,
false, fundOrder => fundOrder.CustomerId == this.customerId);

Note:

Silverlight does not support weak references to lambda expressions or anonymous delegates.

For Silverlight, you need to call a separate public method, as shown in the following code example.

public bool FundOrderFilter(FundOrder fundOrder)
{
    return fundOrder.CustomerId == this.customerId;
}
...
FundAddedEvent fundAddedEvent = this.eventAggregator.GetEvent<FundAddedEvent>();

subscriptionToken
= fundAddedEvent.Subscribe(FundAddedEventHandler, ThreadOption.UIThread, false, FundOrderFilter);

Note:
The Subscribe method returns a subscription token of type
Microsoft.Practices.Prism.Events.SubscriptionToken that can be used to remove a subscription to the
event later. This token is particularly useful when you are using anonymous delegates or lambda
expressions as the callback delegate or when you are subscribing the same event handler with different
filters.

Note:
It is not recommended to modify the payload object from within a callback delegate because several threads could
be accessing the payload object simultaneously. You could have the payload be immutable to avoid concurrency
errors.

Subscribing Using Strong References

If you are raising multiple events in a short period of time and have noticed performance concerns with them, you may
need to subscribe with strong delegate references. If you do that, you will then need to manually unsubscribe from the
event when disposing the subscriber.

By default, CompositePresentationEvent maintains a weak delegate reference to the subscriber's handler and filter
on subscription. This means the reference that CompositePresentationEvent holds on to will not prevent garbage
collection of the subscriber. Using a weak delegate reference relieves the subscriber from the need to unsubscribe and
allows for proper garbage collection.

However, maintaining this weak delegate reference is slower than a corresponding strong reference. For most
applications, this performance will not be noticeable, but if your application publishes a large number of events in a
short period of time, you may need to use strong references with CompositePresentationEvent. If you do use
strong delegate references, your subscriber should unsubscribe to enable proper garbage collection of your subscribing
object when it is no longer used.

To subscribe with a strong reference, use the keepSubscriberReferenceAlive parameter on the Subscribe method,
as shown in the following code example.

FundAddedEvent fundAddedEvent = eventAggregator.GetEvent<FundAddedEvent>();
bool keepSubscriberReferenceAlive = true;

fundAddedEvent.Subscribe(FundAddedEventHandler, ThreadOption.UIThread,
keepSubscriberReferenceAlive, fundOrder => fundOrder.CustomerId == _customerId);

The keepSubscriberReferenceAlive parameter is of type bool:

  1. When set to true, the event instance keeps a strong reference to the subscriber instance, thereby not allowing it to get garbage collected. For information about how to unsubscribe, see the section "Unsubscribing from an Event" later in this topic.
  2. When set to false (the default value when this parameter omitted), the event maintains a weak reference to the subscriber instance, thereby allowing the garbage collector to dispose the subscriber instance when there are no other references to it. When the subscriber instance gets collected, the event is automatically unsubscribed.

Default Subscriptions

For a minimal or default subscription, the subscriber must provide a callback method with the appropriate signature
that receives the event notification. For example, the handler for the TickerSymbolSelectedEvent requires the
method to take a string parameter, as shown in the following code example.

public void Run()
{
    ...
    this.eventAggregator.GetEvent<TickerSymbolSelectedEvent>().Subscribe(ShowNews);
}
public void ShowNews(string companySymbol)
{
    articlePresentationModel.SetTickerSymbol(companySymbol);
}

Unsubscribing from an Event

If your subscriber no longer wants to receive events, you can unsubscribe by using your subscriber's handler or you
can unsubscribe by using a subscription token.

The following code example shows how to directly unsubscribe to the handler.

FundAddedEvent fundAddedEvent = this.eventAggregator.GetEvent<FundAddedEvent>();

fundAddedEvent.Subscribe(FundAddedEventHandler, ThreadOption.PublisherThread);
fundAddedEvent.Unsubscribe(FundAddedEventHandler);

The following code example shows how to unsubscribe with a subscription token. The token is supplied as a return
value from the Subscribe method.

FundAddedEvent fundAddedEvent = this.eventAggregator.GetEvent<FundAddedEvent>();

subscriptionToken
= fundAddedEvent.Subscribe(FundAddedEventHandler, ThreadOption.UIThread, false, fundOrder => fundOrder.CustomerId == this.customerId);
fundAddedEvent.Unsubscribe(subscriptionToken);

More Information

For more information about weak references, see "Weak References" on MSDN:
http://msdn.microsoft.com/en-us/library/ms404247.aspx .

转载于:https://www.cnblogs.com/csw71107313/archive/2013/03/28/2987143.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值