Silverlight 3 WCF-Enabled CodeSnippet app with Amazon SimpleDb

An Amazon SimpleDb Silverlight WCF app

Among Amazon’s multiple cloud offerings is “SimpleDb” – a cloud database service where the data is hosted on Amazon servers. SimpleDb is not a relational database; it stores items in what Amazon refers to as a Domain.

Each “item” has key-value pairs called attributes, along with a Guid unique key. You could probably think of a Domain as a kind of big Dictionary of whatever type you are working with. Since there are no “tables” as in the sense of say, SQL Server, there are no relations between tables or joins.

However, the Amazon SimpleDb service provides an API that includes a Select syntax that is remarkably similar to the SQL Statements you are used to writing, so the learning curve is extremely easy. Many common SQL keywords are available, so, for example, you could write a query using the “INTERSECT” keyword that queries on multiple conditions.

You start by signing up with Amazon Web Services here, and you get an Application Key and a “Secret key” that you’ll use in your applications. Amazon charges by what it calls “block usage”, but don’t worry. With moderate usage you will never get billed. Of course, if you develop and publicly deploy an Amazon SimpleDb application and it becomes very popular, then you have a financial decision to make. But for most applications, that’s not a worry. The downloadable solution that I provide at the end of this article has placeholders in the web.config appSettings section where you can insert your own keys, and off to the races you go!

SimpleDb is fast, and Amazon provides both C# and VB.NET libraries that make using the service very uncomplicated.

I started out by asking myself, “What would I use this for?”. Obviously, yet another “phonebook” application wouldn’t be very interesting. The answer came pretty fast – how about a simple CodeSnippet repository where I can store various code snippets I need to have access to, along with a title and a set of searchable “tags”, or keywords.

So I started out with a simple ASP.NET app to test the various API Methods. Then, I reasoned that I’d like to have a Silverlight version. So I created a Silverlight-enabled WCF service that simply “wraps” the needed API calls. In the solution I’m providing, there is already a copy of the Amazon.SimpleDb project that Amazon supplies, so I won’t go into detail on that.

Once you’ve created your project, you need to reference the Amazon.SimpleDb project, and include the following namespaces:

using Amazon.SimpleDB;
using Amazon.SimpleDB.Model;
using Amazon.SimpleDB.Mock;
using Amazon.SimpleDB.Util;

To use any of the API methods, we first connect to the Service:

string key = ConfigurationManager.AppSettings["AmazonKey"];
string secretKey = ConfigurationManager.AppSettings["AmazonSecretKey"];
var client = new AmazonSimpleDBClient(key, secretKey);

After authentication, we can use the client object to call any of the SimpleDB methods.

The first step in order to use our new Domain is to create it. This is a one – time operation:

var createRequest = new CreateDomainRequest();
createRequest.
DomainName = TextBox1.Text;
CreateDomainResponse
response = client.CreateDomain(createRequest);

The response object for each operation will have various useful fields you can capture, such as the unique identifier for the object. Once the domain is created, we can begin calling various operations on it. For example, here is a complete "putRequest" that creates a new CodeSnippet Item and stores it in the cloud:

protected void Button1_Click(object sender, EventArgs e)
{
string key = ConfigurationManager.AppSettings["AmazonKey"];
string secretKey = ConfigurationManager.AppSettings["AmazonSecretKey"];
var client = new AmazonSimpleDBClient(key, secretKey);

var putRequest = new PutAttributesRequest();
var attributes = new List<ReplaceableAttribute>();
attributes.Add(new ReplaceableAttribute().WithName("Title").WithValue(txtTitle.Text));
string allText = txtSnippet.Text;
if (allText.Length > 1024)
{
double numTimes = (double) allText.Length/1024;
// does it have a fraction? add 1 if it does to get the very last chunk <1024 characters
if (numTimes.ToString().Contains("."))
numTimes++;
for (int i = 0; i < numTimes; i++)
{
attributes.Add(
new ReplaceableAttribute().WithName("Snippet" + i.ToSTring()).WithValue(allText.Substring(i, 1024)));
}
}
else
{
attributes.Add(new ReplaceableAttribute().WithName("Snippet").WithValue(allText));
}


string tags = txtTags.Text;
char[] delim = {','};
foreach (string tagName in tags.Split(delim))
{
attributes.Add(new ReplaceableAttribute().WithName("Tags").WithValue(tagName.Trim()));
}
putRequest.ItemName =
Guid.NewGuid().ToString();
putRequest.
Attribute = attributes;
putRequest.
DomainName = "CodeSnippets";
PutAttributesResponse
response = client.PutAttributes(putRequest);
Label1.
Text = response.ResponseMetadata.RequestId;
}
}

You can see some "funky code" in the above method. It seems that the maximum length for an attribute is 1024 characters. So, if my code snippet is longer, I just "chunk it up" into multiple attributes (Snippet0, Snippet1, etc.). On the way back, it's easy to use .Contains("Snippet") to reassemble the text. An annoyance, certainly, but not a show-stopper.

And here is an example of doing a Search on stored Snippets using the SQL-like Select query:

protected void Button1_Click(object sender, EventArgs e)
{
string srch = TextBox1.Text;

string key = ConfigurationManager.AppSettings["AmazonKey"];
string secretKey = ConfigurationManager.AppSettings["AmazonSecretKey"];
var client = new AmazonSimpleDBClient(key, secretKey);
var selectRequest = new SelectRequest();
selectRequest.SelectExpression = "SELECT * FROM CodeSnippets where Title LIKE '%" + srch + "%' OR Tag ='" +
srch + "'";

SelectResponse response = client.Select(selectRequest);
List<Item> items = response.SelectResult.Item;
var displayItems = new List<DisplayItem>();
foreach (Item itm in items)
{
var disp = new DisplayItem();
disp.Id = itm.Name;
foreach (Attribute att in itm.Attribute)
{
if (att.Name == "Title")
{
disp.Title = att.Value;
}
if (att.Name == "Tags")
disp.Tags += att.Value + " ";
if (att.Name.Contains("Snippet"))
disp.Snippet += att.Value + " ";
}
displayItems.Add(disp);
}
GridView1.DataSource = displayItems;
GridView1.DataBind();
}

You can scale up to millions of items with this ultra-simple cloud database service. And if you decide later that you want to add additional attributes (think: "columns") all you have to do is modify your putRequest and everything will work out fine.

The final step in my SimpleDB experiment was to implement the Silverlight-enabled WCF service and add a SilverLight 3 Navigation app to the solution. The service is ultra-simple:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.ServiceModel;
using System.ServiceModel.Activation;
using Amazon.SimpleDB;
using Amazon.SimpleDB.Model;
using Attribute=Amazon.SimpleDB.Model.Attribute;

namespace CodeSnippets
{
[ServiceContract(
Namespace = "CodeSnippetService")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class CodeSnippetService
{
[OperationContract]
public CreateDomainResponse CreateDomain(string domainName)
{
string key = ConfigurationManager.AppSettings["AmazonKey"];
string secretKey = ConfigurationManager.AppSettings["AmazonSecretKey"];
var client = new AmazonSimpleDBClient(key, secretKey);
var createRequest = new CreateDomainRequest();
createRequest.
DomainName = domainName;
CreateDomainResponse
response = client.CreateDomain(createRequest);
return response;
}

[OperationContract]
public List Search(string searchTerm)
{
string key = ConfigurationManager.AppSettings["AmazonKey"];
string secretKey = ConfigurationManager.AppSettings["AmazonSecretKey"];
var client = new AmazonSimpleDBClient(key, secretKey);
var selectRequest = new SelectRequest();
selectRequest.SelectExpression =
"SELECT * FROM CodeSnippets where Title LIKE '%" + searchTerm +
"%' OR Tag ='" + searchTerm + "'";
SelectResponse
response = client.Select(selectRequest);
List items = response.SelectResult.Item;
var displayItems = new List();
foreach (Item itm in items)
{
var disp = new DisplayItem();
disp.
Id = itm.Name;
foreach (Attribute att in itm.Attribute)
{
if (att.Name == "Title")
{
disp.
Title = att.Value;
}
if (att.Name == "Tags")
disp.Tags += att.
Value + " ";
if (att.Name.Contains("Snippet"))
disp.Snippet += att.
Value + " ";
}
displayItems.
Add(disp);
}

return displayItems;
}

[OperationContract]
public string PutRequest(string title, string codeSnippet, string tags)
{
string key = ConfigurationManager.AppSettings["AmazonKey"];
string secretKey = ConfigurationManager.AppSettings["AmazonSecretKey"];
var client = new AmazonSimpleDBClient(key, secretKey);

var putRequest = new PutAttributesRequest();
var attributes = new List();
attributes.Add(new ReplaceableAttribute().WithName("Title").WithValue(title));
string allText = codeSnippet;
if (allText.Length > 1024)
{
double numTimes = (double) allText.Length/1024;
// does it have a fraction? add 1 if it does to get the very last chunk <1024 characters
if (numTimes.ToString().Contains("."))
numTimes++;
for (int i = 0; i < numTimes; i++)
{
attributes.Add(
new ReplaceableAttribute().WithName("Snippet" + i).WithValue(allText.Substring(i, 1024)));
}
}
else
{
attributes.Add(new ReplaceableAttribute().WithName("Snippet").WithValue(allText));
}


string thetags = tags;
char[] delim = {','};
foreach (string tagName in thetags.Split(delim))
{
attributes.Add(new ReplaceableAttribute().WithName("Tags").WithValue(tagName.Trim()));
}
putRequest.ItemName =
Guid.NewGuid().ToString();
putRequest.
Attribute = attributes;
putRequest.
DomainName = "CodeSnippets";
PutAttributesResponse
response = client.PutAttributes(putRequest);
return response.ResponseMetadata.RequestId;
}


[OperationContract]
public List DisplayResults(string searchTerm)
{
string srch = searchTerm;

string key = ConfigurationManager.AppSettings["AmazonKey"];
string secretKey = ConfigurationManager.AppSettings["AmazonSecretKey"];
var client = new AmazonSimpleDBClient(key, secretKey);
var selectRequest = new SelectRequest();
selectRequest.SelectExpression =
"SELECT * FROM CodeSnippets where Title LIKE '%" + srch +
"%' OR Tags LIKE '%" + srch + "%'";

SelectResponse
response = client.Select(selectRequest);
List items = response.SelectResult.Item;
var displayItems = new List();
foreach (Item itm in items)
{
var disp = new DisplayItem();
disp.
Id = itm.Name;
foreach (Attribute att in itm.Attribute)
{
if (att.Name == "Title")
{
disp.
Title = att.Value;
}
if (att.Name == "Tags")
disp.Tags += att.
Value + " ";
if (att.Name.Contains("Snippet"))
disp.Snippet += att.
Value + " ";
}
displayItems.
Add(disp);
}
return displayItems;
}
}
}

You can download the entire solution including the Silverlight 3 Client app here.

By Peter Bromberg   Popularity  (3435 Views)