Following up on Part I which described the workings of the Resource Locator Framework (RLF), in this post we will create a search application that uses the RLF to collate search results from various RSS-feed providers. The RFL will be used to earmark and resolve (at runtime) all the various providers, and results from which will be displayed in a tabular format using a MVVM type solution. Additionally, we'll make use of Rx-framework like Observables to asynchronously gather and compose search results.
View the sample application here and the source code is also available at Codeplex.
Getting The Search Providers Results
In our sample application the provides are essentially search-engines that output results in RSS-feed format, and within our application they are represented by an ISearchProvider interface (shown below). The search results are materialized into the SearchResult type, which corresponds to an RSS-feed item and has Title, Description, Dated and Url fields. Further, for searching a provider takes the search keywords, and returns an IObservable - using which we can have the results asynchronously pushed to us.

The IObservable/IObserver pairing is also very simple, just to recap for those unfamiliar - you can think of the Observable as the publisher and Observer as the consumer. When the consumer wants to consume, it subscribes to the publisher and the publisher can do three specific things - push a series of values (of an agreed type i.e. type T, via the OnNext method), indicate that it is done (via the OnCompleted method) or if an error were to occur relay that (using the OnError method, which also stops the process). Further, when we subscribe we get an invoke-able token (IDisposable type) which the consumer can (prior to completion) use to opt-out/unsubscribe from the publisher's output.
Now in our case, the ISearchProvider is the publisher, which when asked (using search keywords) publishes a series of search results to which we subscribe. And in our sample we have realized the publishing-consuming using nRoute's build-in IObservable<T> and IObserver<T> implementations called RelayObservable<T> and RelayObserver<T> classes. Below is the outline of ISearchProvider's Search method's implementation which pushes the WebClient's response to the RelayObserver<T>'s subscribers.
1: var _observable = new RelayObservable<SearchResult>();
2: ...
3: _webClient.DownloadStringCompleted += (s, e) =>
4: {
5: if (e.Error != null) // incase of an error
6: _observable.OnError(e.Error);
7: else if (e.Cancelled) // if cancelled we indicate completed
8: _observable.OnCompleted();
9: else { // else, we parse and push the results
10: ParseAndPushResults(e.Results, _observable.OnNext);
11: _observable.OnCompleted(); // we also indicate we are done
12: }
13: }
14: ...
15: return _observable; // return the observable
On the other end of the stick, when we want to consume the output of the publisher above we have to subscribe - and for that we can either use a RelayObserver<T> or equally use the Subscribe extension method on any IObservable<T>. So below we are subscribing to the ISearchProvider's Observable and thereon we handle the three possible return cases. Also note the cancellation token (IDisposable) variable which we keep around incase we want to cancel.
1: var _cancellationToken = _searchProvider.Search(SearchText).Subscribe<SearchResult>(
2: (r) => _results.Add(r), // on result
3: (e) => ShowError(e), // on error
4: () => _isLoading = false); // on completed
And that's it - we have a bona fide asynchronous communication archetype to handle search results in our ViewModel. The rest of the job just involves monkey code to marshal the results to the UI. Also note, because Silverlight can't directly access web-based RSS-feeds (unless the sources opt-in), we have got local ASP.NET proxies to go with the providers.
Getting The Search Providers Themselves
Given we know how to get the search results, our next step is get the providers themselves - and for that we'll create a specific mapping which locates ISearchProvider resources/providers. With the RLF, my general practice to create explicit mappings in a three step process - first create a meta-data class (if needed), create the IResourceLocator, and third create the mapping attributes. We'll cover these steps point-by-point:
Step 1. Create the Metadata Class
The metadata is many-times optional but in this case we want to get a Title for the search provider and an Icon (path) to go with that. Plus, for ease of use we also store the name of the provider and the implementation type in the meta-class itself.
1: public class SearchProviderMeta
2: {
3: public SearchProviderMeta(Type providerType, string name,
4: string title, string iconPath) { ... }
5:
6: public Type ProviderType { get; private set; }
7:
8: public string Name { get; private set; }
9:
10: public string Title { get; private set; }
11:
12: public string IconPath { get; private set; }
13: }
I think that was painlessly-simple, but the idea is you should be able to use the meta in a strongly-typed fashion.
Step 2. Create the Resource Locator
As you know a resource locator is just an implementation of IResourceLocator interface, and here we have a very straightforward implementation for the search providers.
Our search provider's IResourceLocator implementation (DefaultSearchProviderLocator class) shown above is quite simple, it returns an instance of the meta-class we created earlier, it also provides the name of the resource, and by initializing the provider-type it can return instances of the provider. Here actually the implementation of IResourceLocator is numbingly-simple but in other cases the Resource Locator can do all kinds of fuzzy things - it's an abstraction that allows each type of resource or even each individual resource to have its own realization strategy. To give you an example of the fuzziness, the default View Services locator in nRoute can look into the Visual Tree and provide you an instance from there or if applicable a resource can opt to register/unregister an instance of itself as it sees fit (see this post for more info).
Step 3. Create the Resource Mapping
The third and final step is to create an attribute that earmarks the resource as being a search provider, for this we have to derive from the MapResourceBase attribute.
Above our MapSearchProvider attribute in its constructor takes in the name, title and icon path information, using which it creates the meta-class from Step 1. Next, from the base class we override the GetResourceType method which tells the RLF as to which type of resource we are mapping - the answer of course is ISearchProvider type here. Note, the targetType parameter tells us onto which type this attribute is applied on - which in this case has to be our provider type and we duly check for that. We also override the GetResourceLocator to which we return our DefaultSearchProviderLocator from Step 2 - and this then resides in the Resource catalog for ISearchProvider type. I think it is fairly simple, despite the explicit steps involved - however. if you wanted something more ready-to-eat in that case you can use the MapResource attribute or MapService attribute and forge custom mapping.
Earmarking and Consuming The Providers
We've now got the requisite resource mapping and locating functionality in place, and we can just tack the MapSearchProvider attribute and be off to business, so for example:
1: [MapSearchProvider("BingProvider", "Bing Search",
2: "/nRoute.Samples.SearchProviders;Component/BingLogo.jpg")]
3: public class BingProvider : ISearchProvider
4: {
5: ...
6: }
Now to individually retrieve the BingProvider instance or its locator you can use the following code, note "BingProvider" is the resource identifier name:
1: // Gets the resource
2: ResourceLocator.GetResource<ISearchProvider>("BingProvider");
3: // Gets the resource locator
4: ResourceLocator.GetResourceLocator<ISearchProvider>("BingProvider");
However, for use in our ViewModel we are interested in getting all the ISearchProvider types registered - not just one. For that we can access the resource catalog (note, the RLF creates individual catalogs per resource type), and we can also listen to any changes as the catalog implements INotifyCollectionChanged. The Resource<T> is a singleton class that holds the catalog, and the ResourceLocator static class helps with resolving, checking for individual resources or their representative locators.

Using the two classes above we get hold of all the locators, and then rig up columns per-provider to show the results - we make use of the metadata held by the locator to display the icon and title of each provider. The locator also acts like a factory for ISearchProvider instances (per search), which we use to get the search results.
Dynamically Getting More Search Providers
The point of mapping using the RLF is that all the resources are discovered and cataloged at runtime, however the fun doesn't have to stop there. RLF also supports dynamically downloading and mapping all earmarked resources within a DLL or XAP file. To demonstrate that with our sample app, we have a two providers housed in an external DLL which on-demand downloaded and mapped - and because the ViewModel is listening changes to the catalog it is immediately reflected in the UI (try the "+ Provider" button). So to download and automatically map resources we use the RemoteResourceLocator class, have a look:
1: var _remoteResourceUri = new Uri("nRoute.Samples.SearchProvidersEx.dll", UriKind.Relative);
2: var _resourceLoader = new RemoteResourceLoader();
3: _resourceLoader.LoadResource(_remoteResourceUri);
Alternatively, you can download assemblies/xap-packages yourself and map each assembly (which is the unit of mapping, as it were) using the AssemblyMapper static class. Note, when we map we not only map the Search Providers but all kinds of earmarked resources like Services, Modules, ViewModels, ViewServices etc.
Summary
The point of this sample application was to show how you can at runtime discover and resolve resources using the RLF. Further, by creating custom resource mapping/locators, we were able to precisely materialize resources along with the metadata associated with each individual resource. The process of creating custom mappings involved creating a meta-class, followed by a resource locator (which owns the materialization strategy), and lastly a mapping attribute which brings together the two. We also have the ability to dynamically load remote resources using the build-in loaders, and any earmarked resources therein are automatically registered.
You can view the Sample App here.
and you can download the Sample App source-code here.
Note, for the sample project be sure to build the nRoute.Samples.SearchProvidersEx before running the project (it sends the DLL to the Web Project) and set the Web Project as your startup project.
Posted by Rishi on 04-Jan-10 10:01 AM, 10 Comments