Storing message in table storage

In my previous post I looked at getting started with table storage, in this one we will create a table for our entities and store them. As you’ll see, quite easy!

So, to store an entity in table storage you start by creating a TableServiceEntity derived class (recap from previous post):

public class MessageEntity : TableServiceEntity
{
  public MessageEntity() { }

  public MessageEntity(string partitionKey, string rowKey, string message)
    : base( partitionKey, rowKey )
  {
    Message = message;
  }

  public string Message { get; set; }
}

 

You also need a table class, this time deriving from TableServiceContext:

public class MessageContext : TableServiceContext
{
  public const string MessageTable = "Messages";

  public MessageContext(string baseAddress, StorageCredentials credentials)
    : base(baseAddress, credentials)
  {
  }
}

TableServiceContext requires a base address and credentials, and since our class derives from it we need a constructor taking the same arguments. I also have a const string for the table name.

If the table doesn’t exist yet you should create it. This is easy, just add the code to create the table in the MessageContext’s static constructor:

static MessageContext()
{
  var tableClient =  MyStorageAccount.Instance.CreateCloudTableClient();
  tableClient.CreateTableIfNotExist(MessageTable);
}

A static constructor is automatically called when you use the type. Note that I use the MyStorageAccount class, which uses the same static constructor trick to initialize the storage account.

public static class MyStorageAccount
{
  public static string DataConnection = "DataConnection";

  public static CloudStorageAccount Instance
  {
    get
    {
      return CloudStorageAccount.FromConfigurationSetting(DataConnection);
    }
  }

  static MyStorageAccount()
  {
    CloudStorageAccount.SetConfigurationSettingPublisher(
      (config, setter) =>
      {
        setter(
          RoleEnvironment.IsAvailable ?
            RoleEnvironment.GetConfigurationSettingValue(config)
            :
            ConfigurationManager.AppSettings[config]
        );

        RoleEnvironment.Changing += (_, changes) =>
        {
          if (changes.Changes
                     .OfType<RoleEnvironmentConfigurationSettingChange>()
                     .Any(change => change.ConfigurationSettingName == config))
          {
            if (!setter(RoleEnvironment.GetConfigurationSettingValue(config)))
            {
              RoleEnvironment.RequestRecycle();
            }
          }
        };
      });
  }
}

 

Now we are ready to add the code to create and add a message to our table. Add following code to MessageContext:

public static MessageEntity CreateMessage( string message )
{
  return new MessageEntity(MessageTable, Guid.NewGuid().ToString(), message);
}

public void AddMessage(MessageEntity msg)
{
  this.AddObject(MessageTable, msg);
  this.SaveChanges();
}

 

The CreateMessage method creates a new MessageEntity instance, with the same partition key (I don’t expect to store a lot of messages), a unique Guid as the row key, and of course the message. The AddMessage method adds this entity to the table, and then calls SaveChanges to send the new row to the table. This mechanism uses the same concepts as WCF Data Services.

In the previous post we created a web site with a textbox and a button. Implement the button’s click event as follows:

protected void postButton_Click(object sender, EventArgs e)
{
  string message = messageText.Text;
  var msg = MessageContext.CreateMessage(message);
  context.AddMessage(msg);
}

This will allow you to add messages to storage.

Before you can run this sample, you also need to setup the connection. Double-click the CloudMessages project beneath the Roles folder.

image

This open the project’s configuration window. Select the Settings tab and add a “DataConnection” setting, select “Connection String” as the type and then select your preferred storage account. In the beginning it is best to use development storage, and that is what I did here:

image

After running the web site you are of course wondering if your messages were actually added. So let’s add some code and UI to display the messages in the table.

Start by adding the following property to MessageContext:

public IQueryable<MessageEntity> Messages
{
  get { return CreateQuery<MessageEntity>(MessageTable); }
}

This property returns an IQueryable<MessageEntity>, which is then used by LINQ for writing queries. To actual query is performed in our web page class. But first we need to add some UI to display the messages. Add a repeater control beneath the TextBox and Button:

<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
  <p>
    <asp:TextBox ID="messageText" runat="server" Width="396px"></asp:TextBox>
    <asp:Button ID="postButton" runat="server" OnClick="postButton_Click" Text="Post message" />
  </p>
  <p>
    <asp:Repeater ID="messageList" runat="server">
      <ItemTemplate>
        <p>
          <%# ((MessagesLib.MessageEntity) Container.DataItem).Message %>
        </p>
      </ItemTemplate>
    </asp:Repeater>
  </p>
</asp:Content>

 

Now that we can display the messages, let’s add a LoadMessages method below the click event handler of the page:

private void LoadMessages()
{
  var query = from msg in context.Messages
                           select msg;
  messageList.DataSource = query.ToList()
                                .OrderBy(m => m.Timestamp)
                                .Take(10);
  messageList.DataBind();
}

Call this method in the Load event of the page:

protected void Page_Load(object sender, EventArgs e)
{
  if (!IsPostBack)
  {
    LoadMessages();
  }
}

 

And again in the button’s click event:

protected void postButton_Click(object sender, EventArgs e)
{
  string message = messageText.Text;
  var msg = MessageContext.CreateMessage(message);
  context.AddMessage(msg);
  LoadMessages();
}

 

Run. Add some messages, and see them listed (only the first 10 messages will be displayed, change to query as you like).