Cache Persistence and Partitioning
The web service example provided with this article stores cached data as disk files. By default, it uses a local folder, though—providing the account you run the web service under has the relevant permissions—there is no reason why the cache root could not be on a mapped or remote drive. Another possibility is to use the web service to store the cached data in a database, or in any other persistent storage you require.
You may even decide to implement a separate component, business logic layer, or data tier to handle the actual storage. In this way, the web service is simply a communication conduit that interfaces the Caching Application Block with a local or remote backing store of your choosing.
The sample provider stores the cached data as disk files (using the same approach as my previous article), placing them in a subfolder under the root caching folder. However, one interesting feature (see Figure 1) of the sample application is the ability to define a partition or filter value that the web service can use to implement multiple separate caches without requiring you to implement multiple web services. In the configuration of the web service Cache Provider, you can specify a name for the partition that this provider will use.
The partition name gets sent to the web service with every call from the Caching Application Block. The web service can use the partition name to store cached items in different folders on disk, in different database tables, or under different keys—depending on how you choose to implement persistent storage. The sample web service stores cached data in subfolders that correspond to the partition name.
Creating a Web Proxy Class for the Caching Provider
The first—and in fact the trickiest—issue in creating the provider turned out to be implementation of a suitable web service proxy. Initially, I wanted to implement a system where users could create a custom proxy and install it with the web service backing store provider simply by specifying the proxy class type within the Enterprise Library configuration.
However, this approach turned out to be difficult, made even more so because the backing store provider would need to instantiate the proxy without previously knowing the type. While you can instantiate the proxy in that way using the Activator.CreateInstance method, figuring out how to integrate varying classes, types, and properties from a custom proxy class seemed unduly complicated.
Generating a Web Proxy Class Dynamically
One approach to creating the proxy would be to generate it dynamically from the web service Description Language (WSDL) document exposed by the target web site. There are well-known techniques for this; described, for example, in the MSDN article "Calling an Arbitrary Web Service." Roman Kiss wrote a comprehensive and useful article on this technique as well. You can even get a ready-built component from GotDotNet to generate web services from WSDL.
However, it soon became clear that this approach was also overkill. The interface for the proxy and the corresponding interface of the web service are fixed by the requirements of a Caching Application Block backing store provider. So there's no need to generate the proxy dynamically; the WSDL would be the same every time.
User-defined or Built-In Proxy?
Other issues are determining how much variability and configurability should be available for the target web service, and making it configurable within the backing store provider, as these affect the proxy implementation. For example, should the configuration support user-defined namespaces for the web service methods? Or should it be possible to specify the namespace and the target URL at runtime?
In the end, after much consideration, it became clear that—except for the target web service URL—the proxy does not actually need to be configurable by users. Hard-coding the namespace binding into the proxy is not unreasonable, because users can specify the required namespace binding within their custom web service classes.
Finally, because the interface is fixed (based on the methods that a custom backing store must implement), it seemed easier to create a public Interface class that describes the interface. Visual Studio (VS) can then insert stub code for the interface into the web service automatically.
Using the Interface class means the proxy can become a permanent class compiled into Enterprise Library. Users then need only build the web service by implementing the same interface and using the specified namespace binding.
|Author's note: One reason for not offering configurable namespace binding is that I have yet to find a way to dynamically create the WebServiceBindingAttribute required within the proxy class, or change its properties at runtime. The same applies to the SoapDocumentMethodAttribute on each proxy method. Comprehensive searches of the web have not revealed any solution, so perhaps it is not possible.
|Figure 2. Web Service-Based Caching: Here's the overall design and components for the sample web service-based caching mechanism.|
shows the final design of the web service-based caching mechanism in the sample application. You can see that the custom backing store provider, its configuration data storage and design support classes, the proxy class, and its interface are all compiled within the Caching Application Block.
The Caching Web Service class used in the example persists cached data directly (annotated as "A" in Figure 2
). However, there is no reason why it could not communicate with other components or services (annotated as "B" in Figure 2
), and use them to cache the data as required.
Building the Web Proxy Class
The easiest way to create a web service proxy class is to use the WSDL tool provided with Visual Studio. You may need to extract wsdl.exe from your VS 2005 setup files, as it is not installed automatically with the standard installation. You need the two files wsdl.exe and wsdl.exe.config (The WSDL tool is a command-line executable and has no GUI).
To create a proxy class with wsdl.exe, you must provide an XML schema or a WSDL document as the source. Here's one process:
- Design the interface required for communication between the backing store provider and the web service.
- Create an Interface class that defines the communication interface.
- Build a web service containing just the method outlines by implementing this interface within a new ASP.NET Web Service project in Visual Studio 2005.
- Specify the namespace binding you want to use for the proxy and web service in the WebService attribute of the outline web service.
- Install the outline web service on a local web server.
- Execute wsdl.exe, specifying the URL of the outline web service to generate the required proxy class.
- Copy the proxy class into the Caching folder of the Enterprise Library source files (created, by default, in the EntLibSrc folder when you installed Enterprise Library. Rerun the Enterprise Library setup if you did not install them initially).