Supporting cloud and on-premises configuration with a single codebase.
I am frequently asked “How can I build an application on Azure, and use the Azure features (such as blobs, tables and queues), but still have the ability to deploy on premises without having two codestreams?”.
One answer is: you can still use Azure features from your on premises deployment. However, people are generally wary of too much tie-in to the platform, and want the option of deploying without the reliance on any Azure features.
Another answer is; Inversion of Control. If you use a dependency injection framework you can abstract away the implementation of the featues you want to use, and code against a common interface. This means you can work with a variety of underlying technologies, without your code being aware of that detail. The choice would be made by your configuration. I’ll show you how to to it using my favourite IoC framework; Spring.NET.
Start by creating an interface for each of the Azure features you want to use. For a queue, it could look like this:
public interface IQueue { void Push(string message); string Pop(); }
Then create a concrete class that implements this feature for Azure:
public class AzureQueue : IQueue { public AzureQueue(string connectionString) { this.Account = CloudStorageAccount.Parse(connectionString); } public CloudStorageAccount Account { get; private set; } public string QueueName { get; set; } public void Push(string message) { if (null == message) throw new ArgumentNullException("message"); var queue = GetQueue(); queue.AddMessage(new CloudQueueMessage(message)); } public string Pop() { var queue = GetQueue(); var message = queue.GetMessage(); if (null == message) { return null; } queue.DeleteMessage(message); return message.AsString; } private CloudQueue GetQueue() { if (null == this.Account) throw new NullReferenceException("Account"); if (null == this.QueueName) throw new NullReferenceException("QueueName"); var queueClient = this.Account.CreateCloudQueueClient(); var queue = queueClient.GetQueueReference(this.QueueName); return queue; } }
Notice how the connection string is passed in to the constructor, and the queue name is a property. We don’t manually load anything from app settings.
Add the Spring.NET framework using nuget:
Install-Package Spring.CodeConfig
Add a spring section to your config file, and configure your object:
<object id="queue" type="Two10.IoC.AzureQueue" singleton="true"> <constructor-arg name="connectionString" value="UseDevelopmentStorage=true" /> <property name="QueueName" value="test" /> </object>
Notice how the connection string and queue name are set in this configuration.
When you want an instance of the queue, you ask Spring for it:
static void Main(string[] args) { IQueue queue = Create<IQueue>("queue"); queue.Push("test message"); string message = queue.Pop(); Console.WriteLine(message); Console.ReadKey(); } public static T Create<T>(string name) where T : class { IApplicationContext context = ContextRegistry.GetContext(); T obj = context.GetObject(name, typeof(T), null) as T; return obj; }
Using a different queue provider is simply a matter of creating another implementation of IQueue, and modifying the configuration to return that instead of the Azure queue. You could have different configuration files for different builds, and Visual Studio will use the correct set of configuration depending on where the application is deployed.
The other real advantage with this approach is that it makes your code much more testable. By adding a mock for your interface, or a concrete class designed specifically for testing, you can write tests and that run in isolation from Azure.
Download an example project here, which also includes a MemoryQueue implementation.
grahamrhay 11:25 am on November 25, 2011 Permalink | Log in to Reply
XML IoC configuration? What is this, 2003?
Richard 11:42 am on November 25, 2011 Permalink | Log in to Reply
Ha! What would you suggest Ninject or something? Doing all the injection from code is fine, but how would you nicely switch between two concrete classes?
grahamrhay 12:08 pm on November 25, 2011 Permalink | Log in to Reply
You can still use config values (or some other aspect of the environment) when you’re configuring using code or, better yet, conventions. Say no to xml!
Richard 12:17 pm on November 25, 2011 Permalink | Log in to Reply
Are you suggesting reading values from app settings, and adding switch statements to your code is better than xml? I prefer the world as it was in 2003 (but with .NET 4)!
Graham Hay 3:38 pm on November 25, 2011 Permalink | Log in to Reply
I was going to mention the benefits of compile time safety, but that doesn’t really work with conventions 🙂 All the cool kids are doing it?