Building Windows Azure Service Part4: Web Role UI Handler

In this post, you will create the UI that enables the user to perform read, write operations on the GuestBookEntry table. You will update the web role project generated when you created the Windows Azure service. Specifically, you will perform the following tasks:

  • Add a page to the project that contains the UI to display the guest GuestBookEntry table.
  • Create the code that enables the user to store guest information in Table Storage and images in Blob Storage.
  • Finally, configure the storage account used by the web role.

Happy Holidays

ChristmasTree

To render the guest book
  1. In Solution Explorer right-click the GuestBook_WebRole project, select Add Reference.
  2. Add a reference to the Microsoft.WindowsAzure.StorageClient assembly.
  3. Add a reference to the GuestBookData project.
  4. Open the default.aspx file.
  5. Replace the file content with the following markup.
 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="GuestBook_WebRole._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="https://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Windows Azure Guestbook</title>
    <link href="main.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <div class="general">
        <div class="title">
            <h1>
                Windows Azure GuestBook
            </h1>
        </div>
        <div class="inputSection">
            <dl>
                <dt>
                    <label for="NameLabel">Name:</label></dt>
                <dd>
                    <asp:TextBox 
                       ID="NameTextBox" 
                       runat="server" 
                       CssClass="field"/>
                    <asp:RequiredFieldValidator 
                      ID="NameRequiredValidator" 
                      runat="server" 
                      ControlToValidate="NameTextBox"
                      Text="*" />
                </dd>
                <dt>
                    <label for="MessageLabel">Message:</label>
                </dt>
                <dd>
                    <asp:TextBox 
                       ID="MessageTextBox" 
                       runat="server" 
                       TextMode="MultiLine" 
                       CssClass="field" />
                    <asp:RequiredFieldValidator 
                       ID="MessageRequiredValidator" 
                       runat="server" 
                       ControlToValidate="MessageTextBox"
                       Text="*" />
                </dd>
                <dt>
                    <label for="FileUpload1">Photo:</label></dt>
                <dd>
                    <asp:FileUpload 
                        ID="FileUpload1" 
                        runat="server" 
                        size="16" />
                    <asp:RequiredFieldValidator 
                        ID="PhotoRequiredValidator" 
                        runat="server" 
                        ControlToValidate="FileUpload1"
                        Text="*" />
                    <asp:RegularExpressionValidator 
                        ID="PhotoRegularExpressionValidator"
                        runat="server"
                        ControlToValidate="FileUpload1" 
                        ErrorMessage="Only .jpg or .png files are allowed"
                        ValidationExpression="([a-zA-Z\\].*(.jpg|.JPG|.png|.PNG)$)" />
                </dd>
            </dl>
            <div class="inputSignSection">
                <asp:ImageButton 
                       ID="SignButton" 
                       runat="server" 
                       AlternateText="Sign GuestBook"
                       onclick="SignButton_Click" 
                       ImageUrl="~/sign.png" 
                       ImageAlign="Bottom"  />
            </div>
        </div>
        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
                <asp:DataList 
                   ID="DataList1" 
                   runat="server" 
                   DataSourceID="ObjectDataSource1">
                    <ItemTemplate>
                        <div class="signature">
                            <div class="signatureImage">
                                <a href="<%# Eval("PhotoUrl") %>" target="_blank">
                                    <img src="<%# Eval("ThumbnailUrl") %>" 
                                         alt="<%# Eval("GuestName") %>" />
                                </a>
                            </div>
                            <div class="signatureDescription">
                                <div class="signatureName">
                                    <%# Eval("GuestName") %>
                                </div>
                                <div class="signatureSays">
                                    says
                                </div>
                                <div class="signatureDate">
                                    <%#((DateTime)Eval("Timestamp")).ToShortDateString() %>
                                </div>
                                <div class="signatureMessage">
                                    "<%# Eval("Message") %>"
                                </div>
                            </div>
                        </div>
                    </ItemTemplate>
                </asp:DataList>
                <asp:Timer 
                    ID="Timer1" 
                    runat="server"
                    Interval="15000"
                    OnTick="Timer1_Tick">
                </asp:Timer>
            </ContentTemplate>
        </asp:UpdatePanel>
        <asp:ObjectDataSource 
           ID="ObjectDataSource1"
           runat="server" 
           DataObjectTypeName="GuestBook_Data.GuestBookEntry"
           InsertMethod="AddGuestBookEntry"
           SelectMethod="Select" 
           TypeName="GuestBook_Data.GuestBookEntryDataSource">
        </asp:ObjectDataSource>
    </div>
    </form>
</body>
 </html>
To store guest information in Table and Blob Storage

To allow the user to enter guest information and store the entry in Table Storage and the related image in the Blob Storage, you must execute the following steps.

  1. In Solution Explorer open the default.aspx.cs file.
  2. Replace the file content with the following code.
 using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Net;
using GuestBook_Data;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
using System.Text;

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)
            {
                Timer1.Enabled = true;
            }
        }

     
        protected void SignButton_Click(object sender, EventArgs e)
        {
            if (FileUpload1.HasFile)
            {
                InitializeStorage();
                
                // upload the image to blob storage
         CloudBlobContainer container = blobStorage.GetContainerReference("guestbookpics");
                string uniqueBlobName = string.Format("image_{0}.jpg", 
 Guid.NewGuid().ToString());
                CloudBlockBlob blob = 
container.GetBlockBlobReference(uniqueBlobName);
                blob.Properties.ContentType = FileUpload1.PostedFile.ContentType;
                blob.UploadFromStream(FileUpload1.FileContent);
                System.Diagnostics.Trace.TraceInformation(
"Uploaded image '{0}' to blob storage as '{1}'", 
FileUpload1.FileName, uniqueBlobName);

                // create a new entry in table storage
                GuestBookEntry entry = new GuestBookEntry() { GuestName = 
NameTextBox.Text, Message = MessageTextBox.Text, PhotoUrl = 
blob.Uri.ToString(), ThumbnailUrl = blob.Uri.ToString() };
                GuestBookEntryDataSource ds = new GuestBookEntryDataSource();
                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
                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);
            }

            NameTextBox.Text = "";
            MessageTextBox.Text = "";

            DataList1.DataBind();
        }

        protected void Timer1_Tick(object sender, EventArgs e)
        {
            DataList1.DataBind();
        }

        private void InitializeStorage()
        {
            if (storageInitialized)
            {
                return;
            }

            lock (gate)
            {
                if (storageInitialized)
                {
                    return;
                }

                try
                {
                    // Create a new instance of a CloudStorageAccount object from a specified configuration setting. 
                    // This method may be called only after the SetConfigurationSettingPublisher 
                    // method has been called to configure the global configuration setting publisher.
                    // You can call the SetConfigurationSettingPublisher method in the OnStart method
                    // of the web role or in the Application_Start method in the Global.asax.cs file.
                    // If you do not do this, the system raises an exception 
                    var storageAccount = 
                    CloudStorageAccount.FromConfigurationSetting("DataConnectionString");

                    // create blob container for images
                    blobStorage = storageAccount.CreateCloudBlobClient();
                    CloudBlobContainer container = 
                    blobStorage.GetContainerReference("guestbookpics");
                    container.CreateIfNotExist();

                    // 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.CreateIfNotExist();
                }
                catch (WebException)
                {
                
                    StringBuilder buffer = new StringBuilder();
                    buffer.Append("Storage services initialization failure.");
                    buffer.Append(" Check your storage account configuration settings.");
                    buffer.Append(" If running locally,");
                    buffer.Append(" ensure that the Development Storage service is running.");

                    throw new WebException(buffer.ToString());
                       
                }

                storageInitialized = true;
            }
        }
    }
}
To configure the storage account for the web role

In order for the web role to use the Windows Azure storage services, you must provide account settings as shown next.

  1. In Solution Explorer, expand the Role node in the GuestBook project.
  2. Double click GuestBook_WebRole to open the properties for this role and select Settings tab.
  3. Click Add Settings.
  4. In the Name column, enter DataConnectionString.
  5. 12. In the Type column from the drop-down list select ConnectionString.
  6. 13. In the Value column, from the drop-down list, select Use development storage.
  7. 14. Click OK. Then press Ctrl+S to save your changes.

image

Figure 6 Configuring Storage Account For Web Role

A storage account is a unique end- point for Windows Azure blob, queue and table services. You must create a storage account to use these services. For more information, see Windows Azure Platform

This walkthrough uses the development storage included in the Windows Azure SDK development environment to simulate blob, queue, and table services available in the cloud. Windows Azure by default uses SQL Server Express to simulate these services. You can also use a local instance of the SQL Server. To do so you must define the connection string that the development storage can use to connect to the server. For more information, see Using Windows Azure Development Environment Essentials.

To use the development storage, you set the value of the UseDevelopmentStorage keyword in the connection string for the storage account to true. When you deploy your application to Windows Azure, you need to update the connection string to specify storage account settings including your account name and shared key. For example, <Setting name="DataConnectionString" value="DefaultEndpointsProtocol=https;AccountName=YourAccountName;AccountKey=YourAccountKey" />

For related topics, see the following posts.