It's been a while since my last post but I have to say that many things moved in past 3 months. Today I want to blog about a new cool feature which I added a few days ago : a very basic intra-module communication system which will allow modules to send and receive requests among each other during page life-cycle.
I know you MVC kids won't easily understand what the problem is but for WebForms developers there has always been a a quite annoying problem: when processing web controls, expecially custom controls, those which will be processed earlier in page life-cycle have no way to respond to changes performed by controls which get processed later. While this is acceptable in many cases and that's the way most framework works (including MVC), real big advantage of WebForms is to clear ground for advanced processing. That big advantage over other technologies is someway limited by restricted communications between controls since webcontols usually change their processing based on postback data or query string parameters.
Consider this example: I have many controls on my page and one of them is showing a list of pictures which are tagged. Another controls is displaying a list of blog posts and those are tagged as well. In my page life-cycle, webcontrol for pictures get processed before the one displaying posts so if I decide to select a post to read it, maybe inside the very same window, when my postback event gets processed my list of pictures have already been generated since that webcontrol will be processed earlier. So, in that case, how can I generate a list of pictures based on post tags when a user selects a specific post to read it ?
That seems to be an esotheric question for most Web developers but having such feature would be very good in a WebForms context and that's why Microsoft created its connections in WebParts Framework. Connections just do that: take data from other WebParts to create custom processing.
A MVC kid would probably laugh at this problem and state that he/she could simply generate an AJAX Javascript call to a controller and pass it a post id which could be used to generate a list of pictures to show inside pictures box. Nice and true. Except that you won't have clean and nice separation between modules (webcontrols) and independence about them. MVC solution would make posts list (or page) dependent on pictures while WebForms solution would allow both controls to be 100% independent from each other. Admins could also remove one of them and page will keep working.
So I found a nice solution which is both easy to implement and effective... at least I hope !
Each DiavoloPageBase class now has a ModuleCommunicator class inside of it, which gets initialized on page Init. Each module can subscribe this class to receive notifications from other modules where a notification is simply a combination of a name (roughly equivalent to event name), a dictionary of parameters and a flag which specifies if that call is an AJAX call.
Each module can subscribe to receive notifications (events) by simply:
CType(Page, DiavoloPageBase).ModuleComm.AddNotification(AddressOf EventTarget)
Private Sub EventTarget(ByVal Name As String, ByVal Params As Dictionary(Of String, String), Optional ByVal IsAJAX As Boolean = False)
' Code here
End Sub
First line of code will add a delegate (whose name is EventTarget) to current page module communicator. From now on, EventTarget will receive notifications from other modules and module can trigger a notification event this way:
Dim Params As New Dictionary(Of String, String)()
Params.Add("OrderID", "12345")
CType(Page, DiavoloPageBase).ModuleComm.Notify("ShowOrder", Params, True)
In this example, we create a new dictionary for parameters to pass a new order ID and then we invoke Notify for a "ShowOrder" event (or trigger name, or whatever you want to call it).
Our EventTarget procedure will receive these parameters. Notice that EventTarget will receive ALL notifications and thus it should check event name to understand if it has to handle that specific notification. Also notice that notifications targets (i.e. procedure which will be invoked) cannot modify parameters and pass them on.
Automatic subcriptions
We borrowed this idea from DotNetNuke (thank you guys!) though our implementation is quite different. Basically, a module could implement interface IDiavoloCommModule to be automatically subscribed to notifications. When adding modules, Diavolo checks if module implements that specific interface and, if that's the case, it will add a subscription to mandatory procedure which has been defined by module as part of implementation of IDiavoloCommModule.
That makes subscribing to notifications very easy and straightforward. However, that's not mandatory because, since ModuleCommunicator class is exposed as page property, modules can also decide to explicitly subscribe and unsubscribe. That might be helpful if subscription is only needed as an answer to specific conditions.
Difference among Diavolo implementation and WebParts connections
Since the purpose of this new extension is somewhat close to WebParts connections, what's the difference between them ? Well, Diavolo Module Communicator is less formal and, in my opinion, less flexible than our implementation which is quite open. WP connections goal is to automatically share data among controls and they require a less flexible approach which force developers to decorate their code with specific attribute. This is less flexible because :
- consumers just pull data out of providers: nothing more;
- connection only happens during page pre-rendering;
- you cannot unsubscribe because your connection has been in code via attributes;
- you only have one way to communicate, i.e. from providers to consumer, and nothing else.
In our implementation, notifications can happen anytime during page life-cycle and notifier aren't just providing data to consumers. In WP implementation, my consumer gets all relevant data in a property which also requires (in most cases) to define an interface to give shared data a structure, as long as you don't need a primitive type. Diavolo implementation is less formal since you could stick all information you want inside a dictionary. Purists would say formal is better but I'd say that flexible is better.
Moreover, with connections you only have one way to communicate and a single provider with multiple consumers which, by the way, must be aware of providers in order to properly function. Our implementation doesn't require module to be aware of each other but just to agree on a name and parameters. Knowing that a blog module would broadcast blog post ID when displaying its content, another module only need to know trigger name and parameter name to receive such notification.
Modules can freely communicate among each other as there's not a clean separation between providers and consumers: modules can invoke each other multiple times. For example, blog module could broadcast post ID it is showing and a pictures gallery module could use post tags to display relevant pictures but at a later time in page life-cycle, another module could use same tags to download a RSS feed and discover new tags which could be relevant for the same post and broadcast them as well. Gallery module could then expand / refine its search for pictures based on those new tags and a message from a module appearing later in page life-cycle. That looks flexible to me !
Comparing to DNN module communication
Things are very close to DNN implementation but again our implementation is less formal and more open. However, a cool idea for DNN is to be able to pass sender control reference which might be useful in some cases. That's something we could decide to implement at a later time. However, we will probably keep our ability to pass a dictionary of strings in order to let module basically communicate anything.
Other than that, idea behind Diavolo Module Communicator is very close to DNN one.