Spiking around Notification and Generics Part I

As part of the new work we are doing in the new Composite WPF, we’re looking at notification (Pub/Sub). In the CAB world, the way we handled notification was via our EventBroker functionality which provides a loosely coupled mechanism for notification.

Essentially the way it works is this, to publish, you create an event, then you decorate the event with the [EventPublication] attribute. As part of the attribute you specify an event topic, which is simply a string that represents the name of the event. You also specify a scope which is either the current WI, global or module level. For the subscription,  you create a handler either in the same or a different module and decorate it with the [EventSubscription] attribute. In the subscription you specify the topic, as well as the which thread you’d like to have the event marshalled to.

At runtime, the EventBroker strategy plugged into ObjectBuilder strategies come into play to wire up the publishers and subscribers in an indirect fashion. The actual brokering is handled by the root WorkItem so don’t go bother looking for an EventBroker, because you won’t find one :). The Root WI is subscribed to all the event publishers and holds WeakRef pointers to all the event subscribers. Then whenever the publisher event is fired, the Root WI handles that event and then invokes the delegates on each of the references. There are other ways to do this without having OB do all the magic for you and you can wire things up manually to the Root WI yourself. The most common method is through the autowiring.

Here is an illustration of how this all works

image_thumb8_thumb[3]

Examples of how this looks in code are below.

[EventPublication("topic://EventBrokerQuickStart/StartProcess", 
   PublicationScope.WorkItem)]
public event EventHandler<DictionaryEventArgs> StartProcess;

[EventSubscription("topic://EventBrokerQuickStart/StartProcess", 
   Thread = ThreadOption.Background)]
public void StartProcessHandler(object sender, DictionaryEventArgs args)

One of the major benefits of EventBroker is that it keeps the publishers and subscribers completely agnostic from one another, unlike the traditional .NET event model in which the publisher explicitly has references to all of it’s subscribers. The facility we provided in CAB was very powerful, however there have been more than a few criticisms such as those in Jeremy’s “Build your own CAB” post. Within the post he makes the following main points about our implementation.

Too complicated / difficult to debug, relying on attribute magic.
Too generic with semantics that do not clearly indicated intention.

I have also heard these additional concerns.

  • Too tightly coupled with CAB.
  • Mixing of concerns, should not be handled by the WorkItem.
  • Scopes are either too granular or not needed.
  • Performance is impacted by having to use reflection on each build-up to locate publishers and subscribers.

Jeremy then goes on in his post to describe how he implemented event aggregation in StoryTeller using two sets of interfaces, one for the publisher and one for the subscriber. The subscriber interface simply contains an interface that defines a specific for a set of methods for handling the notifications, for example Queued, Completed, etc.

public interface ITestObserver {   
   void StateIsUnknown(Guid testId);
   void StartedRunning(Guid testId);  
   void Completed(TestResult result);
   void Queued(Guid testId);
}

The publisher interface includes methods for firing each of the tests.

[PluginFamily("Default", Scope = InstanceScope.Singleton)]
public interface ITestEventPublisher {
   void RegisterForTestEvents(ITestObserver observer, Test test);
   void UnRegister(ITestObserver observer, Test test);   
   void PublishResult(Test test, TestResult result);   
   void MarkAsQueued(Test test);  
   void MarkAsUnknown(Test test);   
   void MarkAsExecuting(Test Test);   
   TestResult GetLastResult(Guid testId);
}

 

You can see the parameters for the methods on the observer are not the same as those passed in to the publisher. This decoupling is a good thing in that it allows the publisher to transform the data it receives before it gets to the subscribers. In the case of CAB this is not possible since the signatures have to match and the whole pipeline of the event is invisible from the caller.

Within the publisher he keeps a dictionary of observers. As an observer subscribes for notification, they are added to the dictionary. Then In each of the publishing methods he reads through that dictionary of observers and then invokes the correct method based on what is being published. For example below, is the code for MarkAsQueued.

public void MarkAsQueued(Test test) {   
   test.Status = TestStatus.Queued;
   foreach (ITestObserver observer in GetObservers(test))   {
      observer.Queued(test.TestId);   
   }
}

The benefit of this explicit strongly typed approach is immediately obvious when you see how cleanly the code looks for subscribing, notifying and handling the events.

publisher.RegisterForTestEvents(this, subject);
publisher.MarkAsQueued(test1);

public void Queued(Guid testId)
{   
   ...
}

this as opposed to

[EventPublication("topic://StoryTeller/Queued", PublicationScope.WorkItem)]
public event EventHandler<Test> MarkAsQueued;

[EventSubscription("topic://StoryTeller/Queued", Thread = ThreadOption.Background)]
public void QueuedHandler(object sender, Test args)

Jeremy’s version for one thing has type safety. Because everything is strong-typed, if you make a change to the signature of say Queued, then the code actually breaks at compile time. Not so for the EB case, where if I change the signature completely, it won’t break until runtime. Another benefit is that the event signatures are easy to browse and locate. I can even put the interfaces in a namespace that will be easy to discover. Using the class viewer / intellisense, I can quickly see what the available notifications and the parameters are. However, in the case of EB, I have to know by a constant name, which associated class represents the event args. This requires that I have some kind of convention, keep a master list somewhere or hunt through the code to track down which signature to use. As CAB applications span many modules (assemblies), this could be an arduous task.

And so armed with all the feedback and Jeremy’s post, I decided to spike for Composite WPF to see if we could come up with a more loosely coupled and intuitive method for notification than what we had delivered in CAB. What came out of that spike was a generic Notification service that allows publishers and subscribers to explicitly wire themselves up. I took advantage of generics and .NET 3.5 lambda magic to “have my cake and eat it too” providing the benefits of Jeremy’s approach without having to hand-code the publication mechanism.

I also introduced a generic scope concept to allow you to have notifications at a particular scope level. Scoping is a important part of the CAB infrastructre, however it was limited. In this case the Scope is entirely generic allowing you to be as coarse or fine-grained as you see necessary and to choose what you use to represent Scope. Scopes are hierarchical so I can have a ParentScope that contains a ChildScope. This means when I publish at the ParentScope all the subscribers in the ParentScope and ChildScope will be notified. If however I only publish at the ChildScope than only the ChildScope will be notified. To put this in the real world, imagine a batch of orders being one scope, and each order within the batch being a separate child scope. When I notify the batch, all the orders also get notified.

You’ll notice when you look at the mechanism that the Notification service uses the same signature for the publisher and subscriber as it is generic. This however can easily be the infrastructure that is then called by something similar to the ITestEventPublisher described in Jeremy’s post.

Anyway, I’ll discuss how all this works in a later post. It’s late and i need to get to bed. But to wet your appetite, here’s the code from one of the unit tests. I’ve also attached the code.

 

[TestMethod]public void When_Publish_Method_Is_Called_At_Parent_Scope_Then_
   Child_Scope_Is_Invoked()
{   
   MockRepository mocks = new MockRepository();
   NotificationService<string> publisher = new NotificationService<string>("parent");

   IStockPriceEventManager parentSubscriber = 
      mocks.CreateMock<IStockPriceEventManager>();   
   IStockPriceEventManager childSubscriber = 
      mocks.CreateMock<IStockPriceEventManager>();   
   
   StockPriceArgs args = new StockPriceArgs(5);   
   
   Expect.Call(parentSubscriber.StockPriceChanged(args)).Return(true);
   Expect.Call(childSubscriber.StockPriceChanged(args)).Return(true);

   publisher.Subscribe<IStockPriceEventManager>(parentSubscriber, "parent"); 
   publisher.Subscribe<IStockPriceEventManager>(childSubscriber, "child");
   publisher.AddChildScopes("parent", new List<string> { "child" }); 

   mocks.ReplayAll(); 
   
   publisher.Publish<IStockPriceEventManager>(x => x.StockPriceChanged(args), 
      "parent");   
   mocks.VerifyAll();
}
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://code.google.com/p/retlang/ Mike Rettig

    Have you looked at retlang?

    It provides a full featured pub/sub and threading api that you could probably plug into your implementation to handle subscriptions and message delivery.

    Mike Rettig

  • http://www.codebetter.com/blogs/glenn.block/ gblock

    Hi Daniel

    I am all for dynamic languages, though I am still on the fence as to whether they are the panacea of all evils :). Regardless as to whether it was a dynamic languge or not, I think the benefits here still stand.

    1. Having an interface which clearly defines methods and signatures is more intuitive than a generic invoker that i pass a param array to along with a string.
    2. As far as the problem of maintanability of the interfaces as they grow, I agree this is a valid concern. This is why I am becoming more and more a fan of an interface per event. Then my listeners would implement an interface for each event they listen to. Now you may be asking what this provides that the normal eventing model does not. Well, the main thing is that it allows you to quickly now what events a listener subscribes to at the class definition level. Now with EventBroker true I can tell with the attributes, however those attributes are littered all over the code.

    BTW, I am not sure if you looked at the code of my spike (this is a spike), but the model supports using delegates as well.

    3. The cost of event broker is several fold. The largest cost is that OB is reflecting over EVERY object that it builds up to look for those attributes. Secondly, the delegates are getting built up through reflectiion. in the spike code there is no reflecting whatsoever.

    4. I have looked at MqQ, as a matter of fact you are to credit for inspiring the lambdas pattern that i used as I thought of it after reading about MqQ, so thanks! Personally, I like the Rhino Mocks style in that it’s easier to read and less cryptic. If Rhino was not available I would probably use MoQ though, it is very cool.

    Thanks

  • http://www.clariusconsulting.net/kzu Daniel Cazzulino

    Hi Glenn! kzu here :)

    I wonder why people worry so much about being strong-typed in a world that is increasingly favoring dynamic languages and dynamic typing… It’s also telling that the whole extensibility story in Eclipse is based around “magic strings” named extension points yet there’s no popular clamor to make it interface-based and more “safe”/strong-typed. Good documentation is a must, granted, as should be the case with CAB modules too. Good thing is that just by developing a custom plugin to SandCastle or NDoc you would be able to pull that documentation automatically from event broker attributes, and get rid of one more complaint against it :).

    In a heavily pluggable app, the same “safety” you gain with those interfaces plays against independent evolution sooner than later. How are you going to version those events interfaces when a module evolves and publishes more events? I guess you’ll have to either:

    1 – Break existing modules by adding a new member
    2 – Add ITestObserver2, ITestObserver3…. ITestObserverN

    The whole point of EB was to be generic, highly loosely coupled, and very performant. It excels in all aspects IMO. The reflection complaint is a non-issue as EB readily supports receiving event registration information from other sources other than reflection. The OB reflection-based strategy is just a plugin to feed the event manager.

    Have you checked MoQ for simplified mocking? http://code.google.com/p/moq/

    Cheers!