Archive for the 'web services' Category

Published by breki on 27 Oct 2009

C#: Service Caching Proxies With The A Little Help From Some Functional Code

On a current project we’re working on in our company, we are developing a web application which accesses the back-end through some web services. Nothing special really, except that certain web services provide pretty static information like lookup tables, which don’t change very often, so it’s not really necessary to refetch them all the time.

Since we are heavily relying on the dependency injection and all the back-end services are exposed through C# interfaces, it wasn’t really hard to develop a new implementation of such an interface which calls the back-end service and then caches the results in the HTTP cache to be used by any subsequent calls.

Here’s a simple example of a service interface:

public interface IBackendService
{
    List<string> ListSomeStrings(string parameter1, int parameter2);
}

I won’t bore you with an implementation of this, since it’s really a dumb one. What’s interesting is how the cached implementation looks like:

public class BackendServiceCachingProxy : CachingProxyBase<IBackendService>, IBackendService
{
    public BackendServiceCachingProxy(
        IBackendService wrappedService, 
        Cache cache,
        TimeSpan slidingCacheExpiration)
        : base(wrappedService, cache, slidingCacheExpiration)
    {
    }

    public List<string> ListSomeStrings(string parameter1, int parameter2)
    {
        return CallServiceMethod<string>(
            () => ConstructCacheKey("SomeStrings", parameter1, parameter2),
            s => s.ListSomeStrings(parameter1, parameter2));
    }
}

Our caching wrapper inherits from a base class CachingProxyBase. Before showing you the code for CachingProxyBase, let me just quickly explain what the BackendServiceCachingProxy code does.

We’re using HTTP cache, so we have to supply it to the class in the constructor. We also specify how long the cache should be valid (slidingCacheExpiration parameter).

Each service method in the caching wrapper now uses CallServiceMethod method to implement the cached service method calls. You specify two parameters:

  1. A function delegate for constructing the cache key. This key should be unique for each unique combination of method’s input parameters. The CachingProxyBase offers a helper method ConstructCacheKey to help you with this task.
  2. A function delegate for calling the method on the service. This delegate will be used for calling the actual service implementation which will contact the back-end.

 

CachingProxyBase

And now the implementation of the CachingProxyBase:

public abstract class CachingProxyBase<TService>
{
    public void ExpireAllCachedData()
    {
        foreach (System.Collections.DictionaryEntry entry in cache)
            cache.Remove(entry.Key.ToString());
    }

    protected CachingProxyBase(
        TService wrappedService,
        Cache cache,
        TimeSpan slidingCacheExpiration)
    {
        this.wrappedService = wrappedService;
        this.cache = cache;
        this.slidingCacheExpiration = slidingCacheExpiration;
    }

    protected Cache Cache
    {
        get { return cache; }
    }

    protected TimeSpan SlidingCacheExpiration
    {
        get { return slidingCacheExpiration; }
    }

    protected TService WrappedService
    {
        get { return wrappedService; }
    }

    protected TValue CallServiceMethod<TValue>(
        Func<string> constructCacheKeyFunc,
        Func<TService, TValue> serviceMethodFunc)
        where TValue : class
    {
        string cacheKey = constructCacheKeyFunc();

        TValue cachedValue = GetCachedValue<TValue>(cacheKey);

        if (cachedValue == null)
        {
            cachedValue = serviceMethodFunc(WrappedService);
            CacheValue(cacheKey, cachedValue);
        }

        return cachedValue;
    }

    protected string ConstructCacheKey(
        string function, 
        string environmentId, 
        params object[] args)
    {
        StringBuilder cacheKeyBuilder = new StringBuilder();
        cacheKeyBuilder.AppendFormat(
            CultureInfo.InvariantCulture,
            "Proxy-{0}-{1}",
            function,
            environmentId);

        foreach (object arg in args)
            cacheKeyBuilder.AppendFormat(CultureInfo.InvariantCulture, "-{0}", arg);

        return cacheKeyBuilder.ToString();
    }

    private void CacheValue(string cacheKey, object value)
    {
        Cache.Insert(
            cacheKey,
            value,
            null,
            Cache.NoAbsoluteExpiration,
            SlidingCacheExpiration);
    }

    [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
    private T GetCachedValue<T>(string cacheKey) 
    {
        return (T)cache.Get(cacheKey);
    }

    private readonly TService wrappedService;
    private readonly Cache cache;
    private readonly TimeSpan slidingCacheExpiration;
}

TService is a generic parameter representing the service interface the proxy is enhancing with the caching. I think the code is pretty self-explanatory. The ExpireAllCachedData method is exposed to be used in unit tests to make sure the cache is empty before doing any work.

Using It With Windsor Castle

I’ve made a helper method for configuring in Windsor Castle a specific back-end service with or without caching:

[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
protected void RegisterServiceWithOptionalCache<TService, TImplementation, TCachedImplementation>(
    IWindsorContainer container,
    TimeSpan slidingCacheExpiration)
    where TImplementation : TService
    where TCachedImplementation : TService
{
    string serviceName = typeof(TService).Name;

    if (IsCachingUsed)
    {
        container.Register(
            Component.For<TService>().ImplementedBy<TCachedImplementation>()
                .ServiceOverrides(ServiceOverride.ForKey("wrappedService").Eq(serviceName))
                .Parameters(Parameter.ForKey("slidingCacheExpiration").Eq(slidingCacheExpiration.ToString())));

        if (log.IsDebugEnabled)
            log.DebugFormat("Registered {0}", typeof(TCachedImplementation).Name);
    }

    container.Register(
        Component.For<TService>().ImplementedBy<TImplementation>()
            .Named(serviceName));            
}

 

Conclusion

There are probably more elegant ways of doing this (probably using Windsor’s interceptors and reflection), but I think this code does its job well if you have a small number of service methods to cover.

Published by breki on 08 Feb 2008

Friday Goodies – 08. February

Development

Web Services

.NET Development

VisualStudio

GPS

Misc

Published by breki on 31 Jan 2008

Generating WSDL documentation as part of the NAnt build

WSDLs are not very readable, so I wanted to have some sort of developer-friendly documentation for web services as part of our CI build. I found a simple but effective solution: WSDL viewer (by Tomi Vanek), which is an XSLT transformation file that produces nice HTML documentation.

I had to create a custom NAnt task for XSLT transformations since <style> task failed when I tried to do transformations using the WSDL viewer XSLT (it was complaining about some XML elements not being declared):


    <script language="C#" prefix="Brejc.NAntTasks.">
        <code>
            <![CDATA[
   [TaskName("xslt")]
   public class XsltTask : Task
   {
       [TaskAttribute ("inputfile", Required = true)]
       public string InputFile
       {
           get { return inputFile; }
           set { inputFile = value; }
       }

       [TaskAttribute ("outputfile", Required = true)]
       public string OutputFile
       {
           get { return outputFile; }
           set { outputFile = value; }
       }

       [TaskAttribute ("xsltfile", Required = true)]
       public string XsltFile
       {
           get { return xsltFile; }
           set { xsltFile = value; }
       }

       protected override void ExecuteTask ()
       {
           XsltSettings xsltSettings = new XsltSettings (true, true);
           XmlDocument xsltDoc = new XmlDocument();
           xsltDoc.Load (xsltFile);

           XmlUrlResolver resolver = new XmlUrlResolver ();
           XslCompiledTransform transform = new XslCompiledTransform (true);
           transform.Load (xsltDoc, xsltSettings, resolver);

           using (Stream inputStream = File.Open (inputFile, FileMode.Open, FileAccess.Read))
           {
               XmlReader reader = XmlReader.Create (inputStream);
               using (XmlWriter writer = XmlWriter.Create (outputFile))
                   transform.Transform (reader, writer);
           }
       }

       private string inputFile;
       private string outputFile;
       private string xsltFile;
   }
             ]]>
        </code>
        <references>
            <include name="System.Xml.dll"></include>
        </references>
        <imports>
            <import namespace="System.IO"></import>
            <import namespace="System.Xml"></import>
            <import namespace="System.Xml.Xsl"></import>
        </imports>
    </script>

Now all you need is to use this task to create the documentation:


    <target name="docs.wsdl" description="generates wsdl based on the existing web services">
        <mkdir dir="doc\wsdl" unless="${directory::exists(‘doc\wsdl’)}">
        <xslt inputfile="SomeWebService.wsdl">
              outputfile="doc\wsdl\SomeWebService.html"
              xsltfile="lib\WsdlViewer\wsdl-viewer.xsl"/>
    </xslt>
</mkdir></target>