Registering open generic types in Autofac 1.4

by Alex Meyer-Gleaves 3 January 2010 - 12:34 AM

UPDATE (5 January 2010): This feature has now been added to the Autofac 1.4 codebase. I had intended to get this one directly into the codebase but Nick and I got our wires crossed, and I ended up posting it as an extension instead. Regardless, this post remains a valid example of extending Autofac 1.4. The RegisterClosedTypesOf method will appear on the ContainerBuilder in the next 1.4 maintenance release. Until then you can use the extension below to register your open generic types.

This is a follow up to my recent post about writing an extension for registering open generic interface types in Autofac 2. The feature described in the first post, along with support for open generic classes, has since been added to the Autofac 2 codebase. You can grab the current 2.1 preview release on the download page and test it out.

I personally feel that the latest preview version of Autofac 2 is stable enough to start using, but I know that even when released not everyone will be able to adopt the new version as soon as they would like. For that reason I have decided to write a similar extension for Autofac 1.4 that supports both open generic interfaces and classes.

Before I move onto the code I would like to draw a distinction between this feature and the RegisterGeneric method found on instances of the the ContainerBuilder class in Autofac 1.4. The RegisterGeneric method allows you to register an open generic type and have a closed generic type created for you when requested. For example, registering List<> and then resolving List<string> will cause Autofac to create a new List<string> instance for you. The difference is that the RegisterGeneric feature does not locate existing types that close the open generic type being registered.

In the unit test below we are ensuring that a closing type is provided for an open generic interface. It is similar to that from the original post, except the extension method is called RegisterClosedTypesOf and extends ContainerBuilder instances instead of RegistrationBuilder instances. The extension method also has a parameter for the assembly that will be scanned to find the closing types. This is different from the Autofac 2 implementation were the assembly is provided to the RegisterAssemblyTypes method, and the containing types are filtered using a delegate provided to the RegistrationBuilder.

[Test]
public void RegisterClosedTypesOf_OpenGenericInterfaceTypeProvided_ClosingGenericTypesRegistered()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assembly assembly = typeof(ICommand<>).Assembly;
    builder.RegisterClosedTypesOf(typeof(ICommand<>), assembly);
    IContainer container = builder.Build();

    Assert.That(container.Resolve<ICommand<SaveCommandData>>(), Is.InstanceOf<SaveCommand>());
    Assert.That(container.Resolve<ICommand<DeleteCommandData>>(), Is.InstanceOf<DeleteCommand>());
}

In addition to the types I used for unit testing in the previous post, there are two new types used for the testing of open generic classes.

/// <summary>
/// An abstract open generic base class.
/// </summary>
public abstract class Message<T>
{
}

/// <summary>
/// A class that closed the open generic type.
/// </summary>
public class StringMessage : Message<string>
{
}

The new message types are used in the next unit test to make sure that support for open generic classes is working.

[Test]
public void RegisterClosedTypesOf_OpenGenericAbstractClassTypeProvided_ClosingGenericTypesRegistered()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assembly assembly = typeof(Message<>).Assembly;
    builder.RegisterClosedTypesOf(typeof(Message<>), assembly);
    IContainer container = builder.Build();

    Assert.That(container.Resolve<Message<string>>(), Is.InstanceOf<StringMessage>());
}

Now that we know how the extension method is used we can move onto the implementation.

/// <summary>
/// Extension methods for the <see cref="ContainerBuilder"/> class.
/// </summary>
public static class ContainerBuilderExtensions
{
    /// <summary>
    /// Scans the types in an assembly and registers those that support any base or interface that closes the 
    /// provided open generic service type.
    /// </summary>
    /// <param name="builder">The container builder being extended.</param>
    /// <param name="openGenericServiceType">The open generic interface or base class type for which implementations will be found.</param>
    /// <param name="assembly">The assembly to scan for the matching types.</param>
    public static void RegisterClosedTypesOf(this ContainerBuilder builder, Type openGenericServiceType, Assembly assembly)
    {
        if (openGenericServiceType == null) throw new ArgumentNullException("openGenericServiceType");

        if (!(openGenericServiceType.IsGenericTypeDefinition || openGenericServiceType.ContainsGenericParameters))
        {
            throw new ArgumentException(
                string.Format("The type '{0}' is not an open generic class or interface type.",
                              openGenericServiceType.FullName));
        }

        foreach (Type candidateType in assembly.GetTypes())
        {
            Type closedServiceType;
            if (findAssignableTypeThatCloses(candidateType, openGenericServiceType, out closedServiceType))
            {
                builder.Register(candidateType).As(closedServiceType);
            }
        }
    }

    /// <summary>
    /// Looks for an interface on the candidate type that closes the provided open generic interface type.
    /// </summary>
    /// <param name="candidateType">The type that is being checked for the interface.</param>
    /// <param name="openGenericServiceType">The open generic service type to locate.</param>
    /// <param name="closedServiceType">The type of the closed service if found.</param>
    /// <returns>True if a closed implementation was found; otherwise false.</returns>
    private static bool findAssignableTypeThatCloses(Type candidateType, Type openGenericServiceType, out Type closedServiceType)
    {
        closedServiceType = null;

        if (candidateType.IsAbstract) return false;

        foreach (Type interfaceType in getTypesAssignableFrom(candidateType))
        {
            if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == openGenericServiceType)
            {
                closedServiceType = interfaceType;
                return true;
            }
        }

        return false;
    }

    /// <summary>
    /// Returns the interface and base types that given a type is assignable from.
    /// </summary>
    /// <param name="candidateType">The type to find assignable types for.</param>
    /// <returns>A list of the assignable interface and base types.</returns>
    private static IEnumerable<Type> getTypesAssignableFrom(Type candidateType)
    {
        foreach (Type interfaceType in candidateType.GetInterfaces())
        {
            yield return interfaceType;
        }

        Type nextType = candidateType;
        while (nextType != typeof(object))
        {
            yield return nextType;
            nextType = nextType.BaseType;
        }
    }
}

After the usual sort of argument checking the types in the assembly are enumerated and tested to see if they close the open generic type. The findAssignableTypeThatCloses method does the work of locating a possible match and returns a value indicating if a match was found. When a match is found the out parameter is assigned the closing type that was located, and the registration is added to the ContainerBuilder. The getTypesAssignableFrom method helps out by returning all the interface and base types assignable from the type it is provided.

That is all that is needed to add support for open generic types in Autofac 1.4.

ContainerBuilderExtensions.cs (3.71 kb)

ContainerBuilderExtensionsTests.cs (3.93 kb)

Tags:

Categories: Garage Sale Code | Microsoft .NET | Autofac

Registering open generic interface types in Autofac

by Alex Meyer-Gleaves 19 December 2009 - 7:22 PM

UPDATE (22 December 2009): I have submitted a patch to Nick and this feature has now been added to the Autofac V2 codebase. While including the patch Nick added support for open generic classes and renamed the extension method to AsClosedTypesOf. This post will be left in its current form and remains a valid example of extending Autofac.

There has been some discussion lately around connecting an open generic type to its implementation types in a number of different dependency injection containers including StructureMap and Unity:

It looks like this is not the first time this scenario has been discussed:

I also recently noticed a posting on the Autofac Google Group asking if it was possible for Autofac to automatically register an open generic interface type against its implementations. Nicholas Blumhardt (the creator of Autofac and all round nice guy) suggested that this would be easy to implement using the new RegisterAssemblyTypes method found on the ContainerBuilder in the upcoming V2 release. Since I have been meaning to take a closer look at the preview of Autofac V2, I decided that having a look into solving this would be a good way for me to dip my toes into the water. I decided to limit the scope to supporting only automatic registrations for open generic interface types. There is no doubt you could use the extensibility provided by the RegisterAssemblyTypes method to take things much further and add features like those supported in the Unity Auto Registration library.

Time to introduce some types that will be used in the unit tests. First is the open generic interface type.

/// <summary>
/// An open generic interface type.
/// </summary>
public interface ICommand<T>
{
    void Execute(T data);
}

Next we have a couple of simple types that will be used as the generic type parameters.

/// <summary>
/// A type to use as a generic parameter.
/// </summary>
public class SaveCommandData
{
}

/// <summary>
/// A type to use as a generic parameter.
/// </summary>
public class DeleteCommandData
{
}

To keep things interesting I decided to include an abstract base class that implements the ICommand interface.

/// <summary>
/// An abstract base class that implements the open generic 
/// interface type.
/// </summary>
public abstract class CommandBase<T> : ICommand<T>
{
    public abstract void Execute(T data);
}

There will be two command implementations. The first will directly implement the ICommand interface.

/// <summary>
/// A command class that directly implements the open 
/// generic interface type.
/// </summary>
public class SaveCommand : ICommand<SaveCommandData>
{
    public void Execute(SaveCommandData data)
    {
    }
}

The second will implement the ICommand interface by inheriting from the CommandBase<T> abstract class.

/// <summary>
/// A command class that implements the open generic interface 
/// type by inheriting from the abstract base class.
/// </summary>
public class DeleteCommand : CommandBase<DeleteCommandData>
{
    public override void Execute(DeleteCommandData data)
    {
    }
}

I will use the first unit test to define the name and signature of the extension method that be will added to the RegistrationBuilder. The extension method will be named WhereTypeClosesOpenGenericInterface and will be available on the RegistrationBuilder returned from the RegisterAssemblyTypes method. It will take a single parameter for the Type that represents the open generic interface type that automatic registrations will be created for.

This first unit test will actually ensure that passing a null value as the method parameter will result in an ArgumentNullException being thrown.

[Test]
public void WhereTypeClosesOpenGenericInterface_NullTypeProvided_ThrowsException()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assert.Throws<ArgumentNullException>(() => builder.RegisterAssemblyTypes(typeof(ICommand<>).Assembly).
        WhereTypeClosesOpenGenericInterface(null));
}

The next unit test will ensure that passing a non-generic type into the method will result in an ArgumentException being thrown.

[Test]
public void WhereTypeClosesOpenGenericInterface_NonGenericTypeProvided_ThrowsException()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assert.Throws<ArgumentException>(() => builder.RegisterAssemblyTypes(typeof(ICommand<>).Assembly).
        WhereTypeClosesOpenGenericInterface(typeof(SaveCommandData)));
}

Another simple unit test will ensure that passing in a closed generic type will also result in an ArgumentException being thrown.

[Test]
public void WhereTypeClosesOpenGenericInterface_ClosedGenericTypeProvided_ThrowsException()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assert.Throws<ArgumentException>(() => builder.RegisterAssemblyTypes(typeof(ICommand<>).Assembly).
        WhereTypeClosesOpenGenericInterface(typeof(ICommand<SaveCommandData>)));
}

The last of the boring unit tests ensures that passing in an open generic type that is not an interface will again result in an ArgumentException being thrown.

[Test]
public void WhereTypeClosesOpenGenericInterface_NonInterfaceOpenGenericTypeProvided_ThrowsException()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assert.Throws<ArgumentException>(() => builder.RegisterAssemblyTypes(typeof(ICommand<>).Assembly).
        WhereTypeClosesOpenGenericInterface(typeof(List<>)));
}

Now onto the interesting unit test that will ensure our registrations are wired up correctly. We use our new WhereTypeClosesOpenGenericInterface extension method and pass it the ICommand<> open generic interface type. Obviously when we resolve a closed generic interface type we except the returned instance to be the type that implements it. In the case of the SaveCommand the implementation of the interface is direct, and with the DeleteCommand the implementation of the interface is through its inheritance of CommandBase<T>.

[Test]
public void WhereTypeClosesOpenGenericInterface_OpenGenericInterfaceTypeProvided_ClosingGenericTypesRegistered()
{
    ContainerBuilder builder = new ContainerBuilder();
    builder.RegisterAssemblyTypes(typeof(ICommand<>).Assembly)
        .WhereTypeClosesOpenGenericInterface(typeof(ICommand<>));
    IContainer container = builder.Build();

    Assert.That(container.Resolve<ICommand<SaveCommandData>>(), Is.TypeOf<SaveCommand>());
    Assert.That(container.Resolve<ICommand<DeleteCommandData>>(), Is.TypeOf<DeleteCommand>());
}

Finally we arrive at the implementation code. I looked at the StructureMap implementation before writing this to keep an eye out for details that I might have otherwise forgotten. Doing so seemed like a good idea considering their code has already been put through its paces. Take a quick look over the code and I will explain what is going on below.

/// <summary>
/// Extension methods for the <see cref="RegistrationBuilder{TLimit,TActivatorData,TRegistrationStyle}"/> class.
/// </summary>
public static class RegistrationBuilderExtensions
{
    /// <summary>
    /// Specifies that a type from a scanned assembly is registered if it implements an interface
    /// that closes the provided open generic interface type.
    /// </summary>
    /// <typeparam name="TLimit">Registration limit type.</typeparam>
    /// <typeparam name="TRegistrationStyle">Registration style.</typeparam>
    /// <typeparam name="TScanningActivatorData">Activator data type.</typeparam>
    /// <param name="registration">Registration to set service mapping on.</param>
    /// <param name="openGenericInterfaceType">The open generic interface type for which implementations will be found.</param>
    /// <returns>Registration builder allowing the registration to be configured.</returns>
    public static RegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle>
        WhereTypeClosesOpenGenericInterface<TLimit, TScanningActivatorData, TRegistrationStyle>(
            this RegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle> registration, Type openGenericInterfaceType)
        where TScanningActivatorData : ScanningActivatorData
    {
        if (openGenericInterfaceType == null)
        {
            throw new ArgumentNullException("openGenericInterfaceType");
        }

        if (!(openGenericInterfaceType.IsGenericTypeDefinition || openGenericInterfaceType.ContainsGenericParameters) || !openGenericInterfaceType.IsInterface)
        {
            throw new ArgumentException("The type '" + openGenericInterfaceType.FullName + "' is not an open generic interface type.");
        }

        return registration.Where(candidateType => findInterfaceThatCloses(candidateType, openGenericInterfaceType) != null)
            .As(candidateType => findInterfaceThatCloses(candidateType, openGenericInterfaceType));
    }

    /// <summary>
    /// Looks for an interface on the candidate type that closes the provided open generic interface type.
    /// </summary>
    /// <param name="candidateType">The type that is being checked for the interface.</param>
    /// <param name="openGenericInterfaceType">The open generic interface type to locate.</param>
    /// <returns>The type of the interface if found; otherwise, <c>null</c>.</returns>
    private static Type findInterfaceThatCloses(Type candidateType, Type openGenericInterfaceType)
    {
        if (candidateType.IsAbstract) return null;

        foreach (Type interfaceType in candidateType.GetInterfaces())
        {
            if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == openGenericInterfaceType)
            {
                return interfaceType;
            }
        }

        return (candidateType.BaseType == typeof(object)) 
            ? null
            : findInterfaceThatCloses(candidateType.BaseType, openGenericInterfaceType);
    }
}

First the parameter for the open generic interface type is checked to ensure that it is not null and that it is indeed an open generic interface type. Next we use the RegistrationBuilder instance that is being extended to determine what types we want registered and what their service mappings will be. The important methods on the RegistrationBuilder that enable this are the Where and As methods. The Where method takes a predicate that is used to filter the list of scanned types down to only those you are interested in registering. The As method is used to provide the service mappings for the types that got included for registration after the filter was applied.

The Func<Type,bool> predicate provided to the Where method on the RegistrationBuilder instance utilizes a private method named findInterfaceThatCloses. When the findInterfaceThatCloses method is called it will look for an interface on the type provided as the first parameter, that matches the type provided as the second parameter. In our case we are passing in the candidate type that was provided by the assembly scanning process, and the open generic interface type we are interested in matching. When no matching interface is found null is returned. When used in the delegate parameter provided to the Where method for filtering we check for the return from findInterfaceThatCloses being not null, and use the actual type returned from the method for the delegate parameter provided to the As method for mapping the services. We know that when the As method is called it will only be provided with types that were included by the filter, so we need not worry about receiving a type that does not implement the interface at this point.

The implementation of the findInterfaceThatCloses method ensures that only types which are not abstract are checked for matching interfaces. It then iterates through the available interfaces and checks if any match the open generic interface type provided. If no matching interface is found we recursively check if the type’s base class implements the interface until we reach a type that inherits directly from object.

As you can see Nick has done a great job making Autofac extensible, allowing additional requirements for your container to be met with very little effort on your part. I think the next version of Autofac is shaping up nicely and I look forward to posting more about it in the future.

Tags:

Categories: Garage Sale Code | Microsoft .NET | Autofac

Using REST in NAnt with a custom HTTP Task

by Alex Meyer-Gleaves 18 June 2009 - 12:42 AM

I needed to make HTTP requests to a REST web service from a NAnt script today so I knocked up a custom task. The HttpClient class from the WCF REST Starter Kit that I blogged about previously came in handy to offload most of the heavy lifting, leaving me to worry about the task related implementation details. The task supports all the HTTP methods and allows you to specify the content type and the content itself. You can also retrieve the response content and status code through properties set by the task. This was all achieved with surprisingly little code.

using System;
using System.Collections.Generic;
using System.Net;

using Microsoft.Http;

using NAnt.Core;
using NAnt.Core.Attributes;

namespace AlexMG.NAntTasks
{
    [TaskName("http")]
    public class HttpTask : Task
    {
        private static readonly List<HttpStatusCode> successCodes = new List<HttpStatusCode>
        {
            HttpStatusCode.OK,
            HttpStatusCode.Created,
            HttpStatusCode.Accepted,
            HttpStatusCode.NonAuthoritativeInformation,
            HttpStatusCode.NoContent,
            HttpStatusCode.ResetContent,
            HttpStatusCode.PartialContent
        };

        [TaskAttribute("url", Required = true)]
        [StringValidator(AllowEmpty = false)]
        public string Url { get; set; }

        [TaskAttribute("method", Required = false)]
        [StringValidator(AllowEmpty = true)]
        public string Method { get; set; }

        [TaskAttribute("content", Required = false)]
        [StringValidator(AllowEmpty = true)]
        public string Content { get; set; }

        [TaskAttribute("contenttype", Required = false)]
        [StringValidator(AllowEmpty = true)]
        public string ContentType { get; set; }

        [TaskAttribute("connectiontimeout", Required = false)]
        public int ConnectionTimeout { get; set; }

        [TaskAttribute("responseproperty", Required = false)]
        [StringValidator(AllowEmpty = true)]
        public string ResponseProperty { get; set; }

        [TaskAttribute("statuscodeproperty", Required = false)]
        [StringValidator(AllowEmpty = true)]
        public string StatusCodeProperty { get; set; }

        protected override void ExecuteTask()
        {
            HttpClient client = new HttpClient();
            HttpRequestMessage request = new HttpRequestMessage();

            if (!string.IsNullOrEmpty(Method))
            {
                request.Method = Method;
            }

            request.Uri = new Uri(Url);
            
            if (!string.IsNullOrEmpty(ContentType))
            {
                request.Headers.ContentType = ContentType;
            }

            if (!request.Method.Equals("GET", StringComparison.OrdinalIgnoreCase))
            {
                request.Content = (string.IsNullOrEmpty(Content)) ? HttpContent.CreateEmpty() : HttpContent.Create(Content);
                request.Headers.ContentLength = request.Content.GetLength();
            }
            
            if (ConnectionTimeout != 0)
            {
                client.TransportSettings.ConnectionTimeout = TimeSpan.FromSeconds(ConnectionTimeout);
            }

            Project.Log(Level.Info, "Executing HTTP request.");
            Project.Log(Level.Info, "Url: {0}", request.Uri);
            Project.Log(Level.Info, "Method: {0}", request.Method);
            Project.Log(Level.Info, "Content Type: {0}", request.Headers.ContentType);
            Project.Log(Level.Info, "Connection Timeout: {0}", client.TransportSettings.ConnectionTimeout);

            try
            {
                HttpResponseMessage response = client.Send(request);

                if (FailOnError)
                {
                    response.EnsureStatusIsSuccessful();    
                }

                if (!string.IsNullOrEmpty(StatusCodeProperty))
                {
                    Project.Properties[StatusCodeProperty] = response.StatusCode.ToString();
                }

                if (successCodes.Contains(response.StatusCode) && !string.IsNullOrEmpty(ResponseProperty))
                {
                    Project.Properties[ResponseProperty] = response.Content.ReadAsString();
                }

                Project.Log(Level.Info, "Received HTTP response.");
                Project.Log(Level.Info, "Status Code: {0}", response.StatusCode);
                Project.Log(Level.Info, "Content Type: {0}", response.Headers.ContentType);
            }
            catch (ArgumentOutOfRangeException ex)
            {
                string message = string.Format("The HTTP '{0}' request to '{1}' failed:{2}{3}", Method, Url, Environment.NewLine, ex.Message);
                throw new BuildException(message, ex);
            }
        }
    }
}

Using the task is simple. The only mandatory attribute is url and the default HTTP method is GET. Here is a sample NAnt project showing how to use the <http/> task.

<?xml version="1.0"?>
<project name="Http">
  <http url="http://www.howtocreate.co.uk/operaStuff/userjs/samplexml.xml"
        method="GET"
        contenttype="text/xml"
        connectiontimeout="30"
        responseproperty="response"
        statuscodeproperty="status"
        failonerror="true" />

  <echo message="Response: ${response}" />
  <echo message="Status Code: ${status}" />
</project>

I have attached the source code and task assembly below. The compiled assembly has the Microsoft.Http.dll assembly from the WCF REST Starter Kit merged into it using the ILMerge tool. This makes deployment easier by removing the chance of accidentally forgetting to deploy the dependency. If you are compiling from source code you will need to update the xcopy command in the post-build event to point to the location of your NAnt bin folder.

HttpNAnt Binary.zip (139.62 kb)

HttpNAnt Source.zip (1.06 mb)

Tags: , , ,

Categories: Garage Sale Code | Development Tools | Web Development | Web Services

Velocity DataCache Extension Methods

by Alex Meyer-Gleaves 11 June 2009 - 12:53 AM

If you are using the local cache configuration in Velocity you need to be careful how you use objects returned from the cache. The instance returned is a reference to the object stored inside the local cache, and is not a new instance that has been deserialized from the cache host. This means that if you make a change to the object, the change will be seen by any other caller retrieving the same object from the cache.

The current API does not provide a means to determine if the cache you are using has been configured with local caching. When the local caching option is used the type of the DataCache instance returned from the DataCacheFactory is actually an internal type called Microsoft.Data.Caching.LocalCache. The first extension method called IsLocalCache uses this knowledge to determine if you are working with a local cache.

/// <summary>
///        Determines whether the cache is configured as a local cache.
/// </summary>
/// <param name="dataCache">
///        The data cache being extended.
/// </param>
/// <returns>
///     <c>true</c> if configured as a local cache; otherwise, <c>false</c>.
/// </returns>
public static bool IsLocalCache(this DataCache dataCache)
{
    return (dataCache.GetType().FullName == "Microsoft.Data.Caching.LocalCache");
}

The next extension method is another Get method implementation that uses generics to avoid casting in the calling code. This method also accepts a Func<T> delegate that is used to create the object and add it to the cache if the entry for the specified key is not found. To make sure that the object returned from the local cache is not accidently modified the object is cloned before being returned. You can see in the code below that the cloning is performed in the clone method which I will explain in more detail next.

/// <summary>
///        Gets the cached object for the specified key.
/// </summary>
/// <typeparam name="T">
///        The type of the cached object.
/// </typeparam>
/// <param name="dataCache">
///        The data cache being extended.
/// </param>
/// <param name="key">
///        The key for the cached object.
/// </param>
/// <param name="creator">
///        A <see cref="Func{TResult}"/> delegate used to create the object if not found in the cache.
/// </param>
/// <returns></returns>
public static T Get<T>(this DataCache dataCache, string key, Func<T> creator)
{
    object value = dataCache.Get(key);
    if (value == null)
    {
        value = creator();
        dataCache.Put(key, value);
    }
    return dataCache.IsLocalCache() ? clone((T)value) : (T)value;
}

In the private clone method, I have used the DataContractSerializer class to perform the cloning as it works on types that are not explicitly marked as being Serializable or DataContract. The support for serializing POCO objects was added in .NET Framework 3.5 SP1. You can find out more about it here on Aaron Skonnard’s blog. You can see in the implementation below that the serialization cost can be avoided if the type being cloned is a value type or a string. The string class is not cloned because it is immutable and cannot be modified, and value types are of course always passed by value anyway.

private static T clone<T>(T instance)
{
    Type instanceType = typeof(T);
    if (instanceType.IsValueType || instanceType == typeof(string))
    {
        return instance;
    }

    DataContractSerializer serializer = new DataContractSerializer(instanceType);
    using (MemoryStream memoryStream = new MemoryStream())
    {
        serializer.WriteObject(memoryStream, instance);
        memoryStream.Position = 0;
        return (T)serializer.ReadObject(memoryStream);
    }
}

The final extension method is called CreateKey, and is a simple helper method that can be used to create keys for cache entries. It accepts a params array of object values that are concatenated with a pipe character as a separator between them. The ToString method is called on each object to obtain the string value used in the key.

/// <summary>
///        Creates a key to use for caching objects.
/// </summary>
/// <param name="dataCache">
///        The data cache being extended.
/// </param>
/// <param name="values">
///        The values to build the key from.
/// </param>
/// <returns>
///        A key to use for caching objects.
/// </returns>
public static string CreateKey(this DataCache dataCache, params object[] values)
{
    StringBuilder combinedValues = new StringBuilder();
    foreach (object value in values)
    {
        if (combinedValues.Length > 0)
        {
            combinedValues.Append("|");
        }
        combinedValues.Append(value);
    }
    return combinedValues.ToString();
}

Here are some code samples of how to call the extension methods based on some unit tests I created while writing the extensions. I have attached the code file for the extensions to the end of the post. The actual unit tests are not attached as those require Velocity to be installed and configured appropriately.

// Get the default cache using the factory.
DataCacheFactory factory = new DataCacheFactory();
DataCache cache = factory.GetDefaultCache();

// Test if the cache is configured as a local cache.
Assert.That(cache.IsLocalCache(), Is.True);

// Test that objects returned from the local cache are cloned.
Customer customer1 = cache.Get("customer", () => new Customer {FirstName = "John", LastName = "Smith"});
customer1.FirstName = "Frank";

Customer customer2 = cache.Get("customer", () => new Customer {FirstName = "John", LastName = "Smith"});

Assert.That(customer1.FirstName, Is.EqualTo("Frank"));
Assert.That(customer2.FirstName, Is.EqualTo("John"));

// Test that value types are supported.
int integer = cache.Get("integer", () => 123);
Assert.That(integer, Is.EqualTo(123));

// Test that cache keys are created correctly.
Assert.That(cache.CreateKey("customer", 123), Is.EqualTo("customer|123"));

DataCacheExtensions.cs (2.68 kb)

Tags:

Categories: Garage Sale Code | Microsoft .NET

C# wrapper for the Google AJAX Language API

by Alex Meyer-Gleaves 28 April 2009 - 1:09 AM

Introduction

The Google AJAX Language API allows you to perform text translations using a REST based web service API. Being an AJAX targeted API the web service returns a JSON formatted response that is easy to work with in JavaScript. Working with REST web services and JSON responses in C# is also easy. I decided to check out the API by writing a C# wrapper that would allow me to easily translate a string in any of the supported languages. A quick search will show that I am certainly not the first person to do this, but I don’t care as I wanted to do it my way and for myself.

Response Classes

The DataContractJsonSerializer added to the .NET Framework 3.5 makes it easy to serialize and deserialize between JSON and CLR objects. I created classes to represented the JSON response from the web service. The first is the TranslationResponse. You will notice I have used the DataContract and DataMember attributes so I can map my Pascal cased property names to the camel cased property names used in the JSON response. Even when working with JSON it feels dirty when I don’t follow my naming conventions.

/// <summary>
///     The translation response returned from Google.
/// </summary>
[DataContract(Name = "translateResponse")]
public class TranslationResponse
{
    /// <summary>
    ///     Gets or sets the response data.
    /// </summary>
    [DataMember(Name = "responseData")]
    public ResponseData ResponseData { get; set; }

    /// <summary>
    ///     Gets or sets the response details.
    /// </summary>
    /// <remarks>
    ///     This value is only present when the request fails
    ///     and will contain a diagnostic string.
    /// </remarks>
    [DataMember(Name = "responseDetails")]
    public string ResponseDetails { get; set; }

    /// <summary>
    ///     Gets or sets the response status.
    /// </summary>
    /// <remarks>
    ///     A status other than 200 indicates failure.
    /// </remarks>
    [DataMember(Name = "responseStatus")]
    public int ResponseStatus { get; set; }
}

The second class is ResponseData that contains the translated text and source language if it was automatically detected. This occurs when the source language is not provided in the request.

/// <summary>
///     The data part of the response from Google.
/// </summary>
[DataContract(Name = "responseData")]
public class ResponseData
{
    /// <summary>
    ///     Gets or sets the translated text.
    /// </summary>
    [DataMember(Name = "translatedText")]
    public string TranslatedText { get; set; }

    /// <summary>
    ///     Gets or sets the detected source language.
    /// </summary>
    /// <remarks>
    ///     This value is only present when the source language was not provided
    ///     in the request and needed to be detected automatically.
    /// </remarks>
    [DataMember(Name = "detectedSourceLanguage")]
    public string DetectedSourceLanguage { get; set; }
}

Request Helpers

I created some classes to help make the request simple to construct. The Language class contains a property for each of the supported languages. The properties return two letter ISO language names that are used in the request. I grabbed the list from the documentation and with a quick bit of string replacement had the C# class ready to go. You didn’t really think I typed them all out did you?

/// <summary>
///     The languages supported by the Google AJAX Language API.
/// </summary>
public static class Language
{
    public static readonly string Afrikaans = "af";
    public static readonly string Albanian = "sq";
    public static readonly string Amharic = "am";
    public static readonly string Arabic = "ar";
    public static readonly string Armenian = "hy";
    public static readonly string Azerbaijani = "az";
    public static readonly string Basque = "eu";
    public static readonly string Belarusian = "be";
    public static readonly string Bengali = "bn";
    public static readonly string Bihari = "bh";
    public static readonly string Bulgarian = "bg";
    public static readonly string Burmese = "my";
    public static readonly string Catalan = "ca";
    public static readonly string Cherokee = "chr";
    public static readonly string Chinese = "zh";
    public static readonly string ChineseSimplified = "zh-CN";
    public static readonly string ChineseTraditional = "zh-TW";
    public static readonly string Croatian = "hr";
    public static readonly string Czech = "cs";
    public static readonly string Danish = "da";
    public static readonly string Dhivehi = "dv";
    public static readonly string Dutch = "nl";
    public static readonly string English = "en";
    public static readonly string Esperanto = "eo";
    public static readonly string Estonian = "et";
    public static readonly string Filipino = "tl";
    public static readonly string Finnish = "fi";
    public static readonly string French = "fr";
    public static readonly string Galician = "gl";
    public static readonly string Georgian = "ka";
    public static readonly string German = "de";
    public static readonly string Greek = "el";
    public static readonly string Guarani = "gn";
    public static readonly string Gujarati = "gu";
    public static readonly string Hebrew = "iw";
    public static readonly string Hindi = "hi";
    public static readonly string Hungarian = "hu";
    public static readonly string Icelandic = "is";
    public static readonly string Indonesian = "id";
    public static readonly string Inuktitut = "iu";
    public static readonly string Italian = "it";
    public static readonly string Japanese = "ja";
    public static readonly string Kannada = "kn";
    public static readonly string Kazakh = "kk";
    public static readonly string Khmer = "km";
    public static readonly string Korean = "ko";
    public static readonly string Kurdish = "ku";
    public static readonly string Kyrgyz = "ky";
    public static readonly string Laothian = "lo";
    public static readonly string Latvian = "lv";
    public static readonly string Lithuanian = "lt";
    public static readonly string Macedonian = "mk";
    public static readonly string Malay = "ms";
    public static readonly string Malayalam = "ml";
    public static readonly string Maltese = "mt";
    public static readonly string Marathi = "mr";
    public static readonly string Mongolian = "mn";
    public static readonly string Nepali = "ne";
    public static readonly string Norwegian = "no";
    public static readonly string Oriya = "or";
    public static readonly string Pashto = "ps";
    public static readonly string Persian = "fa";
    public static readonly string Polish = "pl";
    public static readonly string Portuguese = "pt-PT";
    public static readonly string Punjabi = "pa";
    public static readonly string Romanian = "ro";
    public static readonly string Russian = "ru";
    public static readonly string Sanskrit = "sa";
    public static readonly string Serbian = "sr";
    public static readonly string Sindhi = "sd";
    public static readonly string Sinhalese = "si";
    public static readonly string Slovak = "sk";
    public static readonly string Slovenian = "sl";
    public static readonly string Spanish = "es";
    public static readonly string Swahili = "sw";
    public static readonly string Swedish = "sv";
    public static readonly string Tagalog = "tl";
    public static readonly string Tajik = "tg";
    public static readonly string Tamil = "ta";
    public static readonly string Telugu = "te";
    public static readonly string Thai = "th";
    public static readonly string Tibetan = "bo";
    public static readonly string Turkish = "tr";
    public static readonly string Uighur = "ug";
    public static readonly string Ukrainian = "uk";
    public static readonly string Unknown = "";
    public static readonly string Urdu = "ur";
    public static readonly string Uzbek = "uz";
    public static readonly string Vietnamese = "vi";
}

I also created a TextFormat enumeration that allows you to specify the format of the text to be translated. The only choices are HTML or plain text. If the URL argument for “format” is not provided the Language API assumes that the input is plain text.

/// <summary>
///     The format of the text to be translated.
/// </summary>
public enum TextFormat
{
    /// <summary>
    ///     The text to translate is HTML.
    /// </summary>
    Html,

    /// <summary>
    ///     The text to translate is plain text.
    /// </summary>
    Text
}

Making the Request

To make the request I decided to use the HttpClient class from the WCF REST Starter Kit. I have blogged about this class previously and it makes working with REST web services a walk in the park. Before jumping into the actual implementation lets have a look at some sample calling code. The static Google class contains an overloaded Translate method that accepts parameters for the text to be translated, source language, destination language and input text format.

TranslationResponse response = Google.Translate("Hello, world!", Language.English, Language.French, TextFormat.Text);

Console.WriteLine("Status: " + response.ResponseStatus);
Console.WriteLine("Details: " + response.ResponseDetails);
Console.WriteLine("Detected Source Language: " + response.ResponseData.DetectedSourceLanguage);
Console.WriteLine("Translated Text: " + response.ResponseData.TranslatedText);

The Language class can be used to specify the source and destination languages. The language parameters are actually string values and can be provided dynamically if required. Using the Language class is simply a convenience that ensures the language values are correct and makes the method easier to use when the values can be hardcoded.

The TextFormat enumeration is used to indicate the format of the text to be translated. Because the parameter is an enumeration the value provided can never be invalid. Plain text is the default when an overload of the Translate method is used that does not require a value for the parameter.

The Translate method returns the deserialized TranslationResponse. I decided to return the response object as it is difficult to report errors without throwing an exception if only the translated text is returned. The response can also include other useful information and I didn’t want to use out parameters to return it.

If the ResponseStatus property of the response contains a value other than 200 a failure has occurred. This value can be different to the real HTTP status code returned to the HttpClient. If the call “virtually” failed the ResponseDetails property will contain an error message and the ResponseData property will be null. If the call was successful the TranslatedText property of the ResponseData will contain the translated text. The DetectedSourceLanguage property will only contain a value if the source language was not provided and was discovered during translation.

Implementation Details

The Translate method from the Google class can be seen below.

/// <summary>
///     Translates the specified text.
/// </summary>
/// <param name="text">
///     The text to translate.
/// </param>
/// <param name="sourceLanguage">
///     The language to translate from.
/// </param>
/// <param name="destinationLanguage">
///     The language to translate to.
/// </param>
/// <param name="textFormat">
///     The format of the text to be translated.
/// </param>
/// <returns>
///     A response that includes the translated text and status information.
/// </returns>
/// <exception cref="ArgumentException">
///     Thrown if the text to translate or destination language is null or empty.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
///     Thrown if the HTTP response status code is not 200.
/// </exception>
/// <exception cref="ApplicationException">
///     Thrown if the response fails due to a non-communication related problem.
///     The response details returned from Google are included in the exception message.
/// </exception>
/// <remarks>
///     If the source language is not provided it will be automatically detected.
/// </remarks>
public static TranslationResponse Translate(string text, string sourceLanguage, string destinationLanguage, TextFormat textFormat)
{
    if (string.IsNullOrEmpty(text)) throw new ArgumentException("The 'text' parameter is invalid.", "text");
    if (string.IsNullOrEmpty(destinationLanguage)) throw new ArgumentException("The 'destinationLanguage' parameter is invalid.", "destinationLanguage");

    HttpClient client = new HttpClient("http://ajax.googleapis.com/");

    HttpQueryString queryString = new HttpQueryString
    {
        {"v", "1.0"},
        {"hl", Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName},
        {"langpair", string.Format("{0}|{1}", sourceLanguage.ToLowerInvariant(), destinationLanguage.ToLowerInvariant())},
        {"q", text},
        {"format", textFormat.ToString().ToLower()}
    };

    Uri serviceUri = new Uri("ajax/services/language/translate", UriKind.Relative);

    HttpResponseMessage responseMessage = client.Get(serviceUri, queryString);
    responseMessage.EnsureStatusIsSuccessful();

    DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(TranslationResponse));
    return (TranslationResponse)serializer.ReadObject(responseMessage.Content.ReadAsStream());
}

After checking the required arguments have been provided a new instance of the HttpClient is created. The client is provided with the Google API domain name for the base address. Next the HttpQueryString class, also from the starter kit, is used to build the query string. This class takes care of all the encoding and string formatting work.

The query string arguments are straight forward:

  • “v” is the version of the API and is hardcoded to “1.0”.
  • “hl” is the host language which is retrieved from the current thread.
  • “langpair” is a pipe separated pair of two letter ISO language names.
  • “q” is the text to be translated.
  • “format” indicates the format of the text to be translated (HTML or plain text).

A new Uri instance is created with the relative address to the translation web service. When the Get method is called on the HttpClient with the relative URL and query string a HttpResponseMessage is returned. The EnsureStatusIsSuccessful method on the response is called to ensure that the HTTP status code returned is 200 (OK). An ArgumentOutOfRangeException will be thrown if the status code is not 200.

Finally, an instance of the DataContractJsonSerializer is created ready to serialize and deserialize instances of the TranslationResponse type. The HTTP response is retrieved as a Stream and provided to the ReadObject method of the DataContractJsonSerializer. The deserialized response is then returned to the caller.

Summary

The Language API is an example of another service from Google that is both cool and free. The WCF REST Starter Kit makes working with REST web services really simple, and JSON is no longer a format that is only useful to web developers working in JavaScript thanks to the DataContractJsonSerializer. I have attached a Visual Studio 2008 solution with the full source code.

GoogleTranslator.zip (136.21 kb)

Tags: ,

Categories: Garage Sale Code | Web Development | Web Services

Compute any hash for any object in C#

by Alex Meyer-Gleaves 16 April 2009 - 1:36 AM

Approach

The .NET Framework already has many classes for cryptography in the System.Security.Cryptography namespace, so there is no need to worry about the hashing algorithms myself. The cryptography classes supplied by the .NET Framework expect to be given a byte array from which the hash can be computed. So, if I can create a hash from an array of bytes, and can convert any object into an array of bytes, then I will be able to calculate the hash for any object. Also, if I can select the CSP (Cryptographic Service Provider) to use for computing the hash, I can then “Compute any hash for any object”. Well, any hash algorithm supported by the .NET Framework at least.

Serialization

Starting with .NET Framework v3.5 SP1, turning any object into an array of bytes is actually very simple. This is because the DataContractSerializer was updated to allow for the serialization of POCO (Plain Old CLR Objects), and not just those marked with the Serializable or DataContract attributes. This means that we can take any object and serialize it into a MemoryStream, and then feed the array of bytes from the stream into the appropriate CSP to compute the hash.

Cryptographic Service Provider

As I mentioned before, there are many of these providers available in the .NET Framework, including providers for the common MD5 and SHA-1 hashing algorithms. The providers can be broken down as implementing one of two types of hash functions: those that require a key and those that don’t. Those that require a key are used for creating a HMAC (Hash Message Authentication Code), which is a combination of a hashing function and a secret key.

There are two abstract base classes defined for these two types of hashing functions: the HashAlgorithm and KeyedHashAlgorithm classes. The main difference is that the KeyedHashAlgorithm class has a Key property that can be used to set the secret key.

The HashAlgorithm and KeyedHashAlgorithm types.

Classes that derive from KeyedHashAlgorithm often have a constructor that excepts the key as a byte array, but this is for convenience only, and the property can be set at any time. This will make life easier when it comes to implementing support for the two types of hash functions.

Implementation

I decided to take advantage of the new Extension Methods language feature added to C# 3.0 to make the hashing methods available on all objects. The extension method for a normal hash function (one without a key) is below. You can see that the generic type parameter has a constraint indicating that the type argument must derive from HashAlgorithm and have a public parameterless constructor.

public static string GetHash<T>(this object instance) where T : HashAlgorithm, new()
{
    T cryptoServiceProvider = new T();
    return computeHash(instance, cryptoServiceProvider);
}

The extension method for a keyed hash function is similar but includes a byte array parameter for the key, and the type argument must instead derive from KeyedHashAlgorithm allowing the Key property to be set.

public static string GetKeyedHash<T>(this object instance, byte[] key) where T : KeyedHashAlgorithm, new()
{
    T cryptoServiceProvider = new T {Key = key};
    return computeHash(instance, cryptoServiceProvider);
}

Both of the extension methods call a private method to perform the actual serialization and the computation of the hash. This method creates an instance of the DataContractSerializer passing the type the extension method is being applied to into the constructor. A MemoryStream instance is created to hold the serialized representation of the object. The byte array of the stream is passed into the ComputeHash method of the HashAlgorithm instance to compute the hash. Finally, the hash is retrieved from the Hash property and converted to a base 64 string.

private static string computeHash<T>(object instance, T cryptoServiceProvider) where T : HashAlgorithm, new()
{
    DataContractSerializer serializer = new DataContractSerializer(instance.GetType());
    using (MemoryStream memoryStream = new MemoryStream())
    {
        serializer.WriteObject(memoryStream, instance);
        cryptoServiceProvider.ComputeHash(memoryStream.ToArray());
        return Convert.ToBase64String(cryptoServiceProvider.Hash);
    }
}

I also decided to add extension methods for the common MD5 and SHA-1 hash functions. These are simply helpers that call the GetHash<T> extension method providing the appropriate HashAlgorithm type for the type argument. The implementation of these helper methods are an example of how the generic extension methods are called.

public static string GetMD5Hash(this object instance)
{
    return instance.GetHash<MD5CryptoServiceProvider>();
}

public static string GetSHA1Hash(this object instance)
{
    return instance.GetHash<SHA1CryptoServiceProvider>();
}

Below is a simple example of using the GetKeyedHash<T> extension method.

byte[] key = new byte[] {1, 2, 3, 4};
DateTimeOffset now = DateTimeOffset.Now;
string hash = now.GetKeyedHash<HMACMD5>(key);
Console.WriteLine(hash);

For those like to copy and paste, here is the complete solution. I have also attached the C# code file to the end of this post. You will need to add a reference to the System.Runtime.Serialization assembly before compiling.

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Security.Cryptography;

namespace AlexMG.Shared
{
    /// <summary>
    ///     Extension methods applied to the <see cref="object"/> type.
    /// </summary>
    public static class ObjectExtensions
    {
        /// <summary>
        ///     Gets a hash of the current instance.
        /// </summary>
        /// <typeparam name="T">
        ///     The type of the Cryptographic Service Provider to use.
        /// </typeparam>
        /// <param name="instance">
        ///     The instance being extended.
        /// </param>
        /// <returns>
        ///     A base 64 encoded string representation of the hash.
        /// </returns>
        public static string GetHash<T>(this object instance) where T : HashAlgorithm, new()
        {
            T cryptoServiceProvider = new T();
            return computeHash(instance, cryptoServiceProvider);
        }

        /// <summary>
        ///     Gets a key based hash of the current instance.
        /// </summary>
        /// <typeparam name="T">
        ///     The type of the Cryptographic Service Provider to use.
        /// </typeparam>
        /// <param name="instance">
        ///     The instance being extended.
        /// </param>
        /// <param name="key">
        ///     The key passed into the Cryptographic Service Provider algorithm.
        /// </param>
        /// <returns>
        ///     A base 64 encoded string representation of the hash.
        /// </returns>
        public static string GetKeyedHash<T>(this object instance, byte[] key) where T : KeyedHashAlgorithm, new()
        {
            T cryptoServiceProvider = new T { Key = key };
            return computeHash(instance, cryptoServiceProvider);
        }

        /// <summary>
        ///     Gets a MD5 hash of the current instance.
        /// </summary>
        /// <param name="instance">
        ///     The instance being extended.
        /// </param>
        /// <returns>
        ///     A base 64 encoded string representation of the hash.
        /// </returns>
        public static string GetMD5Hash(this object instance)
        {
            return instance.GetHash<MD5CryptoServiceProvider>();
        }

        /// <summary>
        ///     Gets a SHA1 hash of the current instance.
        /// </summary>
        /// <param name="instance">
        ///     The instance being extended.
        /// </param>
        /// <returns>
        ///     A base 64 encoded string representation of the hash.
        /// </returns>
        public static string GetSHA1Hash(this object instance)
        {
            return instance.GetHash<SHA1CryptoServiceProvider>();
        }

        private static string computeHash<T>(object instance, T cryptoServiceProvider) where T : HashAlgorithm, new()
        {
            DataContractSerializer serializer = new DataContractSerializer(instance.GetType());
            using (MemoryStream memoryStream = new MemoryStream())
            {
                serializer.WriteObject(memoryStream, instance);
                cryptoServiceProvider.ComputeHash(memoryStream.ToArray());
                return Convert.ToBase64String(cryptoServiceProvider.Hash);
            }
        }
    }
}

Performance

The most expensive part of calculating the hash values is the serialization. This is not meant to be a replacement for the traditional testing of object equality and should be used carefully due to the serialization cost. I performed some basic testing of the NetDataContractSerializer and DataContractJsonSerializer as well. The DataContractJsonSerializer did not perform well when given large or complex object instances such a sizable DataSet. The NetDataContractSerializer performance was about the same as the DataContractSerializer.

ObjectExtensions.cs (3.30 kb)

Tags:

Categories: Garage Sale Code

Code Tag Plugin for Windows Live Writer

by Alex Meyer-Gleaves 13 January 2009 - 9:21 PM

I made a simple Windows Live Writer plugin that allows you to easily surround text with the <code> HTML phrase tag. Writing a plugin for WLW is fairly straightforward and everything you need to know can be found in the Windows Live Writer SDK documentation on MSDN. The default browser rendering of text inside a <code> tag is to display it using a fixed-width font. You can of course alter its appearance using CSS to make it a little more interesting.

If no text is selected in the editor and the plugin is activated, a small dialog is presented into which you can enter the text that is to be placed inside the tag. You can also select text in the editor and have it immediately placed inside the tag when the plugin is activated. Trailing whitespace in the selected text will not be included inside the tag. Any trailing whitespace inside the tag would also be fixed-width, causing the spacing before and after the text inside the tag to appear uneven.

To install the plugin simply drop the AlexMG.CodeTagPlugin.dll file into the C:\Program Files\Windows Live\Writer\Plugins folder. When building from source code the plugin will be copied to the WLW plugin folder during the post-build event.

<code> Tag Plugin (3.67 kb)

<code> Tag Plugin - Source (5.45 kb)

Tags: ,

Categories: Garage Sale Code

About the author

Alex Meyer-Gleaves I'm a software developer living in Australia (that island like continent in the southern hemisphere). I love Microsoft .NET and C#. I hate early mornings, slow drivers and Lotus Notes.

Google Reader Clips

SpringWidgets
RSS Reader
This widget is the staple of our platform. Read all your feeds right here with thisone widget - Supported feeds are OPML, RSS, RDF, ATOM. Watch your favorite Podcastin the embedded Video Player on the Desktop or publish your own video playlist toyour site for others to view!

Recent Comments

Comment RSS

Links

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in  anyway.

© Copyright 2008