Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save BretStateham/6793709 to your computer and use it in GitHub Desktop.
Save BretStateham/6793709 to your computer and use it in GitHub Desktop.
"Introduction to Cloud Services" Hands-On Lab Completed Code
using Microsoft.WindowsAzure.Storage.Table;
using Microsoft.WindowsAzure.Storage.Table.DataServices;
using System.Linq;
namespace GuestBook_Data
{
public class GuestBookDataContext : TableServiceContext
{
public GuestBookDataContext(CloudTableClient client) : base(client)
{ }
public IQueryable<GuestBookEntry> GuestBookEntry
{
get
{
return this.CreateQuery<GuestBookEntry>("GuestBookEntry");
}
}
}
}
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using System;
using System.Collections.Generic;
namespace GuestBook_Data
{
public class GuestBookDataSource
{
private static CloudStorageAccount storageAccount;
private GuestBookDataContext context;
/// <summary>
/// The static constructor initializes the storage account by reading its settings from the configuration
/// and then uses the CreateTablesFromModel method in the CloudTableClient class to create the tables
/// used by the application from the model defined by the GuestBookDataContext class.
/// By using the static constructor, you ensure that this initialization task is executed only once.
/// </summary>
static GuestBookDataSource()
{
storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("DataConnectionString"));
CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
CloudTable table = cloudTableClient.GetTableReference("GuestBookEntry");
table.CreateIfNotExists();
}
/// <summary>
/// The default constructor initializes the GuestBookDataContext with the CloudTableClient instance
/// returned by the storageAccount that was itself initalized in the class' static constructor.
/// </summary>
public GuestBookDataSource()
{
this.context = new GuestBookDataContext(storageAccount.CreateCloudTableClient());
}
/// <summary>
/// The GetGuestBookEntries method retrieves today's guest book entries by creating a TableQuery operation that filters the retrieved information using the current date as the partition key value. The web role uses this method to bind to a data grid and display the guest book.
/// </summary>
/// <returns>The collection of GuestBookEntries from the partition for the current date.</returns>
public IEnumerable<GuestBookEntry> GetGuestBookEntries()
{
//Shouldn't we replace the following two lines with:
//CloudTable table = context.ServiceClient.GetTableReference("GuestBookEntry");
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference("GuestBookEntry");
TableQuery<GuestBookEntry> query = new TableQuery<GuestBookEntry>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, DateTime.UtcNow.ToString("MMddyyyy")));
return table.ExecuteQuery(query);
}
/// <summary>
/// This method creates a TableOperation to insert the new guest book to storage.
/// </summary>
/// <param name="newItem">The GuestBookEntry to add (insert)</param>
public void AddGuestBookEntry(GuestBookEntry newItem)
{
TableOperation operation = TableOperation.Insert(newItem);
CloudTable table = context.ServiceClient.GetTableReference("GuestBookEntry");
table.Execute(operation);
}
/// <summary>
/// Updates a current GuestBookEntry with the url to the thumbnail image for the entry.
/// This method will most likely be called by a worker role instance that is responsible
/// for creating thumbnail images for new GuestBookEntry instances.
/// </summary>
/// <param name="partitionKey">The Partition Key value of the entity to update</param>
/// <param name="rowKey">The Row Key value of the entity to update</param>
/// <param name="thumbUrl">The URL of the thumbnail image.</param>
public void UpdateImageThumbnail(string partitionKey, string rowKey, string thumbUrl)
{
CloudTable table = context.ServiceClient.GetTableReference("GuestBookEntry");
TableOperation retrieveOperation = TableOperation.Retrieve<GuestBookEntry>(partitionKey, rowKey);
TableResult retrievedResult = table.Execute(retrieveOperation);
GuestBookEntry updateEntity = (GuestBookEntry)retrievedResult.Result;
if (updateEntity != null)
{
updateEntity.ThumbnailUrl = thumbUrl;
TableOperation replaceOperation = TableOperation.Replace(updateEntity);
table.Execute(replaceOperation);
}
}
}
}
using Microsoft.WindowsAzure.Storage.Table;
using System;
namespace GuestBook_Data
{
public class GuestBookEntry : TableEntity
{
public GuestBookEntry()
{
//To partition the data, the GuestBook application uses the date of the entry as the PartitionKey,
//which means that there will be a separate partition for each day of guest book entries.
//In general, you choose the value of the partition key to ensure load balancing of the
//data across storage nodes.
PartitionKey = DateTime.UtcNow.ToString("MMddyyyy");
//The RowKey is a reverse DateTime field with a GUID appended for uniqueness.
//Tables within partitions are sorted in RowKey order, so this will sort the tables into the correct
//order to be shown on the home page, with the newest entry shown at the top.
RowKey = string.Format("{0:10}_{1}", DateTime.MaxValue.Ticks - DateTime.Now.Ticks, Guid.NewGuid());
}
public string Message { get; set; }
public string GuestName { get; set; }
public string PhotoUrl { get; set; }
public string ThumbnailUrl { get; set; }
}
}
using GuestBook_Data;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.Queue;
using System;
using System.IO;
using System.Net;
using System.Web.UI;
namespace GuestBook_WebRole
{
public partial class _Default : System.Web.UI.Page
{
private static bool storageInitialized = false;
private static object gate = new object();
private static CloudBlobClient blobStorage;
private static CloudQueueClient queueStorage;
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
//Start Timer1 when the page is
//retrieved the first time.
//Timer1 will then refresh
//the data in DataList1 every
//15 seconds...
this.Timer1.Enabled = true;
}
}
protected void SignButton_Click(object sender, EventArgs e)
{
if (this.FileUpload1.HasFile)
{
this.InitializeStorage();
// upload the image to blob storage
//Each blob will go in the "guestbookpics" "subfolder"
//Each blob will have a name that is comprised of a GUID and it's original extension.
string uniqueBlobName = string.Format("guestbookpics/image_{0}{1}", Guid.NewGuid().ToString(), Path.GetExtension(this.FileUpload1.FileName));
CloudBlockBlob blob = blobStorage.GetContainerReference("guestbookpics").GetBlockBlobReference(uniqueBlobName);
blob.Properties.ContentType = this.FileUpload1.PostedFile.ContentType;
blob.UploadFromStream(this.FileUpload1.FileContent);
System.Diagnostics.Trace.TraceInformation("Uploaded image '{0}' to blob storage as '{1}'", this.FileUpload1.FileName, uniqueBlobName);
// create a new entry in table storage
//Create a new GuestBook entry (with the url to the Full photo blob uploaded above)
GuestBookEntry entry = new GuestBookEntry() { GuestName = this.NameTextBox.Text, Message = this.MessageTextBox.Text, PhotoUrl = blob.Uri.ToString(), ThumbnailUrl = blob.Uri.ToString() };
//Save the new GuestBookEntry into table storage using the GuestBook_Data.GuestBookDataSource.
GuestBookDataSource ds = new GuestBookDataSource();
ds.AddGuestBookEntry(entry);
System.Diagnostics.Trace.TraceInformation("Added entry {0}-{1} in table storage for guest '{2}'", entry.PartitionKey, entry.RowKey, entry.GuestName);
// queue a message to process the image
//This will write a CloudQueueMessage into the "guestthumbs" queue with all the details a worker role will need
//to create a thumbnail image for the new GuestBookEntry
var queue = queueStorage.GetQueueReference("guestthumbs");
var message = new CloudQueueMessage(string.Format("{0},{1},{2}", uniqueBlobName, entry.PartitionKey, entry.RowKey));
queue.AddMessage(message);
System.Diagnostics.Trace.TraceInformation("Queued message to process blob '{0}'", uniqueBlobName);
}
//Clear out the web form so a new entry can be made without having to delete the old values first
this.NameTextBox.Text = string.Empty;
this.MessageTextBox.Text = string.Empty;
//Rebind the DataList to the Data in TableStorage
//DataList1 is bound to ObjectDataSource1 which uses the
//GuestBook_Data.GuestBookDataSource.GetGuestBookEntries() method
//As it's SelectMethod. Calling DataBind on the DataList then will
//Execute the GuestBook_Data.GuestBookDataSource.GetGuestBookEntries()
//method and return the lastest entries (last row first) from the
//Table storage partion for the current date.
//Wow, a lot of stuff happens with this simple call huh!
this.DataList1.DataBind();
}
protected void Timer1_Tick(object sender, EventArgs e)
{
//Timer1 is declared in the Default.aspx markup.
//It has an interval of 15000 milliseconds, or 15 seconds
//So basically, we'll rebind the DataList1 data every
//15 seconds.
//The datalist is inside an AJAX UpdatePanel so
//this won't cause a full page load, just a partial load.
this.DataList1.DataBind();
}
private void InitializeStorage()
{
//If this method has already been called,
//don't do it again, just exit.
if (storageInitialized)
{
return;
}
//Lock the static "gate" object so that
//we don't accidentally have multiple
//calls to this method running in parallel.
//This could occur if multiple browsers hit the page
//at the same time.
lock (gate)
{
//Again double check that some other thread didn't
//just complete this. If it did, no need to do any more
//just return
if (storageInitialized)
{
return;
}
try
{
// read account configuration settings
var storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("DataConnectionString"));
// create blob container for images
blobStorage = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobStorage.GetContainerReference("guestbookpics");
container.CreateIfNotExists();
// configure container for public access
var permissions = container.GetPermissions();
permissions.PublicAccess = BlobContainerPublicAccessType.Container;
container.SetPermissions(permissions);
// create queue to communicate with worker role
queueStorage = storageAccount.CreateCloudQueueClient();
CloudQueue queue = queueStorage.GetQueueReference("guestthumbs");
queue.CreateIfNotExists();
}
catch (WebException)
{
throw new WebException("Storage services initialization failure. "
+ "Check your storage account configuration settings. If running locally, "
+ "ensure that the Development Storage service is running.");
}
//If we got here, everything worked. Flag the storageIntialized bool as
//true so this method will exit immediately next time it is called.
storageInitialized = true;
}
}
}
}
using GuestBook_Data;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.Queue;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
namespace GuestBook_WorkerRole
{
public class WorkerRole : RoleEntryPoint
{
private CloudQueue queue;
private CloudBlobContainer container;
public override void Run()
{
Trace.TraceInformation("Listening for queue messages...");
while (true)
{
try
{
// retrieve a new message from the queue
CloudQueueMessage msg = this.queue.GetMessage();
if (msg != null)
{
// parse message retrieved from queue
var messageParts = msg.AsString.Split(new char[] { ',' });
var imageBlobName = messageParts[0];
var partitionKey = messageParts[1];
var rowkey = messageParts[2];
Trace.TraceInformation("Processing image in blob '{0}'.", imageBlobName);
string thumbnailName = System.Text.RegularExpressions.Regex.Replace(imageBlobName, "([^\\.]+)(\\.[^\\.]+)?$", "$1-thumb$2");
CloudBlockBlob inputBlob = this.container.GetBlockBlobReference(imageBlobName);
CloudBlockBlob outputBlob = this.container.GetBlockBlobReference(thumbnailName);
using (Stream input = inputBlob.OpenRead())
using (Stream output = outputBlob.OpenWrite())
{
this.ProcessImage(input, output);
// commit the blob and set its properties
outputBlob.Properties.ContentType = "image/jpeg";
string thumbnailBlobUri = outputBlob.Uri.ToString();
// update the entry in table storage to point to the thumbnail
GuestBookDataSource ds = new GuestBookDataSource();
ds.UpdateImageThumbnail(partitionKey, rowkey, thumbnailBlobUri);
// remove message from queue
this.queue.DeleteMessage(msg);
Trace.TraceInformation("Generated thumbnail in blob '{0}'.", thumbnailBlobUri);
}
}
else
{
System.Threading.Thread.Sleep(1000);
}
}
catch (StorageException e)
{
Trace.TraceError("Exception when processing queue item. Message: '{0}'", e.Message);
System.Threading.Thread.Sleep(5000);
}
}
}
public override bool OnStart()
{
// Set the maximum number of concurrent connections
ServicePointManager.DefaultConnectionLimit = 12;
// read storage account configuration settings
var storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("DataConnectionString"));
// initialize blob storage
CloudBlobClient blobStorage = storageAccount.CreateCloudBlobClient();
this.container = blobStorage.GetContainerReference("guestbookpics");
// initialize queue storage
CloudQueueClient queueStorage = storageAccount.CreateCloudQueueClient();
this.queue = queueStorage.GetQueueReference("guestthumbs");
Trace.TraceInformation("Creating container and queue...");
bool storageInitialized = false;
while (!storageInitialized)
{
try
{
// create the blob container and allow public access
this.container.CreateIfNotExists();
var permissions = this.container.GetPermissions();
permissions.PublicAccess = BlobContainerPublicAccessType.Container;
this.container.SetPermissions(permissions);
// create the message queue(s)
this.queue.CreateIfNotExists();
storageInitialized = true;
}
catch (StorageException e)
{
var requestInformation = e.RequestInformation;
var errorCode = requestInformation.ExtendedErrorInformation.ErrorCode;//errorCode = ContainerAlreadyExists
var statusCode = (System.Net.HttpStatusCode)requestInformation.HttpStatusCode;//requestInformation.HttpStatusCode = 409, statusCode = Conflict
if (statusCode == HttpStatusCode.NotFound)
{
Trace.TraceError(
"Storage services initialization failure. "
+ "Check your storage account configuration settings. If running locally, "
+ "ensure that the Development Storage service is running. Message: '{0}'",
e.Message);
System.Threading.Thread.Sleep(5000);
}
else
{
throw;
}
}
}
return base.OnStart();
}
public void ProcessImage(Stream input, Stream output)
{
int width;
int height;
var originalImage = new Bitmap(input);
if (originalImage.Width > originalImage.Height)
{
width = 128;
height = 128 * originalImage.Height / originalImage.Width;
}
else
{
height = 128;
width = 128 * originalImage.Width / originalImage.Height;
}
Bitmap thumbnailImage = null;
try
{
thumbnailImage = new Bitmap(width, height);
using (Graphics graphics = Graphics.FromImage(thumbnailImage))
{
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage(originalImage, 0, 0, width, height);
}
thumbnailImage.Save(output, ImageFormat.Jpeg);
}
finally
{
if (thumbnailImage != null)
{
thumbnailImage.Dispose();
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment