KeiStory

WPF/Prism Event Aggregator

 

EventAggregator 

Prism library 는 application 상에서 느슨하게 커플링된 component 간의 communication 을 위한 event mechanism 을 제공하는데, event aggrgator 서비스에 기반을 둔 이 mechanism 은 publisher 나 subscriber 가 event를 통해 서로간에 직접적인 참조 없이 communication 이 가능하게 해준다.

EventAggregator 는 동시에 데이터를 주고 받을 수 있는 (multicasting) publish/subscribe 기능을 제공하기도 한다. 이 말은 하나의 event 를 발생시키는 여러개의 publisher 가 있을 수도 있고, 하나의 event 를 수신하는 여러개의 subscriber 들이 있을 수 있다는 의미이다. controller 나 presenter 같은 business logic code 사이에서 message 를 보낼 때 그리고 여러 모듈들 사이에서 event 를 발생시키는 EventAggregator 를 생각해보자.

Prism Library 에서 만들어진 Event 는 typed event 이다. 이 말은 application 이 돌기 전에 compile 시점에서 error 를 감지할 수 있다는 의미이다. 또한 Prisim Library 에서 EventAggregator 는 subscriber 나 publisher 가 특정한 EventBase 를 마음대로 위치시킬 수 있도록 해주기도 하며, 여러개의 publisher / 여러개의 subscriber 를 지원 가능하다.

​IEventAggregator

EventAggregator 클래스는 container 에서 service 로서 제공되고, IEventAggregator 인터페이스를 통해 얻을 수 있다. event aggregator 는 event 를 위치시키거나 만들어 내고 event 들의 collection 을 유지하는 역할을 한다.

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

EventAggregator 는 현재 생성되어 있지 않다면, 첫 번째 access 시점에 event 를 생성한다. 이로 인해 publisher 나 subscriber 는 해당 event가 사용가능한지 여부를 결정할 필요가 없다.

PubSubEvent

publisher 와 subscriber 를 실제로 연결해주는 것은 PubSubEvent class 인데, Prism library 안의 유일한 EventBase class 의 구현체이다. 이 class 는 subscriber 의 리스트를 유지 관리하고 subscriber 에거 보내는 event 들을 처리한다.

PubSubEvent class 는 generic type으로 정의된, payload type을 인수로 받는 generic class 인데 compile 시점에 event 연결이 잘 될 수 있도록 publisher 와 subscriber 가 적절한 method 를 제공하게 해준다. 아래는 PubSubEvent class 의 일부 정의를 보여준다.

Note : PubSubEvent 는 Prism.Core NuGet package 에 있는 Prism.Events 라는 namespace 에 정의되어 있다.

 

Creating an Event

PubSubEvent 는 application 또는 module 의 특정 이벤트에 대한 base class 가 되도록 만들어졌다. TPayload 는 event 의 payload 타입인데, payload 라는 것은 event 가 발생했을 때(publish) subscriber 에게 전달되는 argument 이다.

예를 들어, 다음의 코드는 TickerSymbolSelectedEvent 를 보여주고 있는데, payload 는 company symbol를 포함하는 string 값이다. 이 클래스의 구현이 비어 있는 것에 주목하라.

public class TickerSymbolSelectedEvent : PubSubEvent{}

Note : Composite application 상에서 event 들은 주로 여러 module 상에서 공유되는 경우가 많기 때문에 보통은 common 한 곳에 정의한다. "Core" 나 "Infrastructure" 프로젝트 같은 shared assembly 에 정의하는 것이 보통이다.

 

Publishing an Event

Publisher 는 EventAggregator 로부터 event를 받고 publish method 를 호출함으로써 event 를 발생시킨다. EventAggregtor 에 access 하기 위해서는 class constructor 에 IEventAggregator 타입을 인자로 받도록 추가해서 dependency injection 을 사용할 수 있다.

public class MainPageViewModel
{
    IEventAggregator _eventAggregator;
    public MainPageViewModel(IEventAggregator ea)
    {
        _eventAggregator = ea;
    }
}

다음 코드는 TickerSymbolSelectedEvent 를 publish 하는 예를 보여준다.

_eventAggregator.GetEvent().Publish("STOCK0"); ​

 

Subscribing to Events

Subscriber 는 pubSubEvent class 의 overload 가능한 Subscirbe method 중 하나를 이용해서 event 에 참여할 수 있다.

public class MainPageViewModel
{
    public MainPageViewModel(IEventAggregator ea)
    {
        ea.GetEvent().Subscribe(ShowNews);
    }
    void ShowNews(string companySymbol)
    {
        //implement logic
    }
}

PubSubEvents 를 구독할 수 있는 몇 가지 방법이 있다. 최선의 방법을 결정하는데 다음의 기준들이 도움이 될 것이다.

- event 가 발생했을 때 UI Elements 를 update 하는 경우 : UI thread 에서 해당 event 를 받도록 subscribe 한다.

- event 를 filtering 할 경우 : subscribing 할 때 filter delegate 을 제공한다.

- event 관련하여 performance 고려가 필요한 경우 : subscribing 할 때 strongly referenced delegate를 사용하고 PubSubEvent 에서 manually unsubscribing 한다.

- 위의 그 어떤 것도 적용하기 힘든 경우 : default subscription 을 사용한다.

다음 section에서 이러한 option 들에 대해 알아본다.

Subscribing on the UI Thread

subscriber 는 종종 event에 대한 응답으로 UI Elements를 업데이트할 필요가 있는데, WPF에서는 오직 UI thread 만이 UI elements를 업데이트 할 수가 있다.

기본적으로 subscriber는 publisher 의 thread 상에서 event를 받는다. 만약 publisher 가 UI thread 로부터 event를 보내게 되면 subscriber 는 UI를 업데이트 할 수 있다. 하지만 만약 publisher 의 thread 가 background thread 이면 subscriber 는 직접적으로 UI elements 를 업데이트 할 수가 없다. 이런 경우 subscriber 는 Dispatcher class 를 이용해서 UI thread 상에서 업데이트 되도록 scheduling 을 할 필요가 있다.

Prism Library 에서 제공하는 PubsubEvent 는 subscriber가 UI thread 에서 event 를 자동으로 수신하도록 해줄 수 있는데, subscriber는 이 부분을 다음 코드와 같이 subscription 때 명시해 준다.

public class MainPageViewModel
{
    public MainPageViewModel(IEventAggregator ea)
    {
        ea.GetEvent().Subscribe(ShowNews, ThreadOption.UIThread);
    }
    void ShowNews(string companySymbol)
    {
        //implement logic
    }
}

ThreadOption 다음과 같다.

- PublisherThread : publisher의 thread 에서 event 를 받도록 하려면 이 option을 사용한다. (default setting)
- BackgroundThread : .NET Framework thread-pool thread 에서 event 를 비동기적으로 받도록 하려면 이 option을 사용한다.
- UITrhead : UI thread 에서 event 를 받도록 하려면 이 option을 사용한다.

Note : PubSubEvent 가 UI thread 상에서 subscriber 에게 event 를 발생시키도록 하기 위해서는 EventAggregator 가 반드시 UI thread 상에서 초기에 생성되어지도록 해야 한다.

 

Subscription Filtering

subscriber 가 발행된 모든 event들을 하나하나 처리할 필요가 없을 수도 있는데, 이런 상황에서는 subscriber 가 filter parameter 를 사용할 수 있다. filter parameter 는 System.Predicate 형식이고, 발생된 event 의 payload 가 subscriber 의 callback method가 실행되어야 하는 조건인지 아닌지 판단해주는 delegate 이기도 하다. 만약 payload 가 조건에 부합하지 않으면 subscriber의 callback 은 실행되지 않게 된다.

이러한 filter 대게 lambda expression으로 구현된다.

public MainPageViewModel(IEventAggregator ea)
{
    TickerSymbolSelectedEvent tickerEvent = ea.GetEvent(); 
    tickerEvent.Subscribe(ShowNews, ThreadOption.UIThread, false, companySymbol => companySymbol == "STOCK0");
}

 Note : Subscribe method 는 추후 해당 event 에 대한 subscription 을 제거하는데 사용되는 Prism.Events.SubscriptionToken 타입의 subscription token 이라는 것을 리턴한다. 이 token 은, callback delegate 으로서 anonymous delegate(익명 대리자) 또는 lambda expression 을 사용했을 때 특별히 유용하게 사용할 수 있다. 또는 다른 filter 로 동일한 event 를 구독하고 있을 때도 마찬가지이다.

반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band