My previous post detailed the Navigation Services infrastructure, in this post I will get into the Action Services Infrastructure. The Action Services also build above the Routing Services, however it has a markedly different API. The constant is that we use Urls as the primary identifier (optionally with name-value parameters), but the action themselves are executed in fire-and-forget manner. Like before I will take on each part individually, but first below is my mind-map type class diagram for Action Services.Click for full size shot Before we go ahead, let me point out that I've erroneously misspelt definition as "defination" as seen above - I will definitely correct that.  However, I'm quite prone to such mistakes, including all-things related to grammar, spellings, punctuation, syntax and the likes  - it's a known defect, so please do excuse me.

No Returns for a Two-Step Process

In the class diagram above you might have noticed, that we have an IUrlRequest derivate type called ActionRequest but no equivalent IUrlResponse derivative. That is because an Url based action doesn't return a value, think Action<T> delegate - even so there is nothing stopping you from relaying back a response albeit indirectly. Secondly, to get an action registered it is a two step process, one we register a route with an Url and then we register one or more handler against that route. The logic for this disconnection is that we can have zero or more handlers registered against the same route and, equally we can also unregister a handler - think Pub/Sub. The sample code below shows both the deeds:

// We need to register an action url
ActionService.MapAction("CounterAddRoute", "Counters/Add/{CounterName}");

// we can now register a handler
var _thumbsUpHandler = new ThumbsUpCounterHandler();
ActionService.RegisterHandlerByRoute("CounterAddRoute", _thumbsUpCounter);

// we register another counter
ActionService.RegisterHandlerByUrl("Counters/Add/{CounterName}", new ThumbsDownCounterHandler());

// and we can unregister a counter too
ActionService.UnRegisterHandlerByRoute("CounterAddRoute", _thumbsUpHandler);

In the snippet above, we first register an Url with a route name "CounterAddRoute", this helps us reference the route. We then create an instance of the "ThumbsUpCounterHandler" class that implements the IActionHandler interface, which we register as a handler for the route. The next statement shows how we can also register handlers against Urls just as we can against Routes - it is essentially the same thing either way. Also, as shown in the last statement we can also unregister handlers, note there is another way of doing this - which is by naming the handlers while registering so that you can unregister it by the handler name rather than the instance.

 IActionHandlerThe IActionHandler Interface

The IActionHandler interface is a contract that is used to respond to action requests. It's signature is very similar to the INavigationHandler and equally it is also very ICommands'que. Basically we check with the handler if can handle the action, if it can handle then only the handle method is invoked. The handle method also has a very simple signature, it get passed in an IActionDefination (or IActionDefinition) that contains the original request and the parsed parameters. Like everywhere else in the framework the parsed parameters are a merged name-value collection of the original request parameters, the default parameters set while defining the route, and the parsed token-parameters from the Url. And sequence for merging is such that request parameters override Url parsed parameters, which in turn override the default parameters.

One other critical thing to note is that when you register a handler you are registering an implicit singleton instance for handling all action requests, and so you have to be mindful of that in your handler's code. And if this doesn't suit you needs, just wrap it within a factory-style implementation of IActionHandler that can provision as required.

Action Request

Like I've mentioned earlier all requests constitute of two principle things, one the Url and two a name-value collection; as evident in the IUrlRequest interface below. The other member in that interface is the ServiceState property, which is supposed to be for internal use to carry service's own water.  ActionRequest Related Types 
The one thing the ActionRequest type adds is the Dispatcher property, which is self-descriptively a type of the Dispatcher class. This is important because the ActionHandlers are executed off the UI-thread and asynchronously, and to effect any results or changes this provides the bridge back to the UI thread.

Action Route Handler

The Action Service uses a specific IRouteHandler implementation of a type named ActionRouteHandler; instances of this type are registered with the RoutingServices as the handlers for all action related routes. This is non-configurable, because this specific handler is used to maintain a collection of IActionHandler(s) registered for the particular route. You will normally not deal with this class directly, as the ActionService consumes it internally. However it is good know.

 Action ServiceActionServiceClass

The Action Service wraps around the Routing Service, but unlike the Navigation Service it has a fire-and-forget type of consumption semantics. As I've said before, calling on an action doesn't return any value whatsoever, so you might have ten registered handlers for a specific action or you might have zero handlers but there are no indications about the handles count or the result as far consumer's API is concerned. Now, I've explained the two-step registration process, which separates the registration of the route/Url and adding or removing of the related IActionHandlers - think .NET events, where we have the event definition and adding/removing of the event handlers. And you can register or un-register handlers by identifying it through the route name or the mapped Url, either way. Also, you also check if an action is already registered using the IsActionUrlRegistered or IsActionRouteRegistered methods.

The Process method is the entry point to get an action executed, it has two overrides, one that uses the registered handlers and another one which can execute against a given IActionHandler instance. In the second option, only the given handler is called for and not the registered ones, however as of now this is executed synchronously but this will change in the future to an asynchronous execution model. Furthermore, the option that you can execute an action against a handler gives you an almost ICommand like capability, the difference being the loosely-coupled Url based identifier. And particularly for this scenario, you can use the ActionDelegate type which implements the IActionHandler interface which gives you a simple wrapper to use from your ViewModel (by exposing it as a property and binding within the UI). However do note, even if you use your custom handler for an action the Url must have been registered - this is by design.

Attributes Mapping

Like with the Navigation Services, we also get an extensible AOPish style functionality to auto-register actionable Urls. This functionality is accessed by the MapAssemblyActions and MapLoadedAssembliesActions methods on the ActionService class.
 Attributes for Registering Actions
This works in a similar way to the Navigation related attributes, the ActionService when asked to auto-map actions from a given assembly it looks for the MapActionBaseAttribute type on all public types. As you can see the attribute has an Url property and resolves an IActionHandler - which are the two things required to get an action route and handler setup. It is a very simple system, and you can extend this by your custom implementation of the base attribute, to address your custom loading/mapping strategies. The other benefit of using the attributes is that it is not statically registered, rather is runtime based which you exploit to your advantage. For example, if the user is not Admin we don't load and map the actions usable only by the admin or you could load different UIs identifiable by the Url but we just exchange the assembly yet the identifiers stay the same.  Below is a simple snippet from the demo app that gets itself registered via the MapActionHandleAttribute, and is used to navigate externally.

[MapActionHandler("System/NavigateExternal/{URL}")]
public class NavigateExternalAction : IActionHandler
{

    const string URL_KEY = "URL";
    const string ORKPAD_COM = "http://www.orktane.com";
    const string NROUTE_CODEPLEX = "http://nRoute.codeplex.com";

#region IActionHandler Members

    public bool CanHandle(IActionDefination action)
    {
        return true;
    }

    public void Handle(IActionDefination action)
    {

        // we need to use the dispatcher to navigate to external addresses
        action.Request.Dispatcher.BeginInvoke(new Action(() => 
        {

            var _urlKey = action.ParsedParameters[URL_KEY].ToString();

            if (string.Equals(_urlKey, "Orkpad", StringComparison.OrdinalIgnoreCase))
                HtmlPage.Window.Navigate(new Uri(ORKPAD_COM, UriKind.Absolute));

            else if (string.Equals(_urlKey, "nRoute", StringComparison.OrdinalIgnoreCase))
                HtmlPage.Window.Navigate(new Uri(NROUTE_CODEPLEX, UriKind.Absolute));

        }));
    }

#endregion

}

This finishes another part of the laborious documentation, I have three/four more big topics to go. However, I'll take a detour to rather write up samples and a couple of quick starts on the core features. Anyhow, I hope this post underscored how the actions infrastructure in nRoute helps to further and easily "demarkify" business-functionality from the UI.

Comments

Rishi
Rishi
on 14-Mar-09 12:46 PM
@Medications, thanks. Well this post and last couple of posts have been my attempt at generating some kind of documentation. I want to sort of capture the assumptions and ideas behind the code. Anyway, you might be interested in my next couple of posts, they are more consumption oriented rather than documentation type. Have a read, and let me know what you think. Cheers.

Eric Willeke
Eric Willeke United States
on 15-Mar-09 11:40 AM
Keep up the posts, this is great stuff, and I plan on digging into this soon!

Rishi
Rishi
on 15-Mar-09 5:42 PM
@Eric, thanks a ton. Writing these kind of documentation is really wearing (now, I can really appreciate people who do this), but I want to do document it nicely. I have planned to write on couple of topics that include the browser integration stuff, navigation containers, application-level containers, and a big one on M-V-VM.. Still, today I have a little styles related post coming up.. hope you'll like it. Cheers.

trackback
Ork Pad
on 25-Mar-09 10:55 PM
Trackback from Ork Pad

Step into the nRoute "Future Desktop" Demo App

doda
doda United States
on 24-May-09 6:33 PM
This is a very ietresnaya information

trackback
geff zhang
on 08-Jun-09 3:20 AM
Trackback from geff zhang

?? nRoute ??????? Silverlight ?????

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading