Sunday, December 6, 2009

Beginner's Guide to Work With Azure Table Storage and Silverlight Episode 2

In the last post we started a project with cloud Service and ASP.net MVC 2 Application then we connected this Application with a Silverlight Client using WCF Service. Now in this post we will create an Entity in Azure Table and a Silverlight application to Insert Students in Azure Table.

  • Open the Project we Created in last post and in MVC Project add a class and name it Student.cs.
  • Add a using statement in Student.cs
using Microsoft.WindowsAzure.StorageClient;

Now derive class Student.cs from TableServiceEntity now add following properties in Student.
      
public string StudentClass {
            get {
                return PartitionKey;
            }
            set {
                PartitionKey = value;
            }
        }

        public string StudentID {
            get {
                return RowKey;
            }
            set {
                RowKey = value;
            }
        }
        public int Age { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }

  • This class represents the logical model of the Azure Table. We have assigned PartitionKey value of variable named Class, so that Students Studying in a Class will be available on the same node and RowKey is assigned value of StudentID because StudentID is always unique field in a Class.
  • Now we need another class extending TableServiceContext. This class provides the functionality to access the table Storage.
  • Add another class in MVC Project and name it StudentContext.cs and add using statements

using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure;

  • Now derive StudentContext.cs from TableServiceContext and add following constructor in it.
public StudentContext(string BaseAddress, StorageCredentials credentials)
            : base(BaseAddress, credentials)
        {
       
        }

  • Add an assembly named System.Data.Services.Client and a using statement;

                     using System.Data.Services.Client; 

  • Add a Property named StudentsTable which returns IQueryable of type Student:

public IQueryable<Student> StudentsTable
        {
            get {
                return this.CreateQuery<Student>(“StudentsTable”);
            }       
        }

  • In Cloud project Under The Folder Roles right click on StudentRole and select properties. In the Dialog shown click Settings Tab in Side bar then press Add Setting button. Name it DataConnectionString select ConnectionString  in Type Field. Press Browse button at next shown dialog Press OK to select local development account; Save settings (Ctrl+S) and close it.

  • Now Add these statements in WebRole.cs File in MVC Project in OnStart() function before the return statement.

Microsoft.WindowsAzure.CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
            {
                configSetter(Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetConfigurationSettingValue(configName));
            });

  • Now Although we are done and we can start working on Table Storage in our WCF Service but it would good to use a Repository for each Table. So defining a repository for Student Entity. Create a new class in MVC Project Name it StudentRepository.cs.
  • Add following code in it.

using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace StudentRole
{
    public class StudentRepository
    {
        private static CloudStorageAccount storageAccount;
        static StudentRepository()
        {
            storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
            CloudTableClient.CreateTablesFromModel(
                            typeof(StudentContext),
                            storageAccount.TableEndpoint.AbsoluteUri,
                            storageAccount.Credentials);
        }

        public void AddStudent(Student newStudent)
        {
            StudentContext ctx = new StudentContext(storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials);
            ctx.AddObject("StudentsTable", newStudent); //String literal must be the name given to property in StudentContext
            ctx.SaveChanges();
        }

        public IEnumerable<Student> GetAllStudents()
        {
            StudentContext ctx = new StudentContext(storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials);
            var students = from s in ctx.StudentsTable
                           orderby s.StudentID
                           select s;
            return students.ToList();
        }

        public void UpdateStudent(Student newStudent)
        {
            StudentContext ctx = new StudentContext(storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials);
            Student oldStudent = ctx.StudentsTable.Where(
                                        s => s.StudentClass == newStudent.StudentClass
                                                        &&
                                             s.StudentID == newStudent.StudentID).FirstOrDefault();
            if (oldStudent != null)
            {
                oldStudent.Name = newStudent.Name;
                oldStudent.Address = newStudent.Address;
                oldStudent.Age = newStudent.Age;
                oldStudent.StudentClass = newStudent.StudentClass;
                oldStudent.StudentID = oldStudent.StudentID;
                ctx.UpdateObject(oldStudent);
                ctx.SaveChanges();
            }
        }

        public void DeleteStudent(string StudentClass, string StudentID)
        {
            StudentContext ctx = new StudentContext(storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials);
            Student student = ctx.StudentsTable.Where(s => s.StudentID == StudentID && s.StudentClass == StudentClass).FirstOrDefault();
            if (student != null)
            {
                ctx.DeleteObject(student);
                ctx.SaveChanges();
            }
        }
    }
}

  • Now Add following function definitions in IStudentService.cs file.

[OperationContract]
List<Student> GetAllStudents();
[OperationContract]
void InsertStudent(Student st);
[OperationContract]
void UpdateStudent(Student newStudent);
[OperationContract]
void DeleteStudent(string StudentClass, string StudentID);

  • Add following code in StudentService.svc

public class StudentService : IStudentService
    {
        #region IStudentService Members

        public List<Student> GetAllStudents()
        {
            StudentRepository repo = new StudentRepository();
            return repo.GetAllStudents().ToList();
        }

        public void InsertStudent(Student st)
        {
            StudentRepository repo = new StudentRepository();
            repo.AddStudent(st);
        }

        public void UpdateStudent(Student newStudent)
        {
            StudentRepository repo = new StudentRepository();
            repo.UpdateStudent(newStudent);
        }

        public void DeleteStudent(string StudentClass, string StudentID)
        {
            StudentRepository repo = new StudentRepository();
            repo.DeleteStudent(StudentClass, StudentID);
        }
        #endregion
    }

  • Finally All the work at server side is done we have created Entity, Context, Repository and WCF Service having CRUD Functions now we need a client.
  • Open Home.xaml in Silverlight Project replace the Contents with these markup.

<navigation:Page x:Class="AzureTableClient.Home"
    xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
    Title="Home"
    Style="{StaticResource PageStyle}" xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data">

  <Grid x:Name="LayoutRoot">
    <ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource PageScrollViewerStyle}">

      <StackPanel x:Name="ContentStackPanel" Orientation="Vertical">

        <TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}"
                           Text="Home"/>
                <data:DataGrid AutoGenerateColumns="False" Height="100" Name="StudentsDisplay" Width="Auto" IsReadOnly="True" Margin="10,20,10,10" />
                <StackPanel Name="PanelBtns" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,20">
                    <Button Content="List All Students" Height="23" Name="btnListAll" Width="100" Background="#FF25DE25" Margin="5" />
<Button Content="Insert Student" Height="23" Name="btnInsert" Width="100" Background="#FF25DE25" Margin="5" Click="btnInsert_Click" />                    <Button Content="Delete Student" Height="23" Name="btnDelete" Width="100" Background="#FF25DE25" Margin="5" />
                    <Button Content="Update Student" Height="23" Name="btnUpdate" Width="100" Background="#FF25DE25" Margin="5" />
                </StackPanel>
            </StackPanel>

    </ScrollViewer>
  </Grid>

</navigation:Page>

  • Now move to Code behind file and add a using statement:

using AzureTableClient.StudentSvc;

  • Add following code in code behind File.

private void btnInsert_Click(object sender, RoutedEventArgs e)
        {
            StudentServiceClient proxy = new StudentServiceClient();
            proxy.Endpoint.Address = new System.ServiceModel.EndpointAddress("
http://localhost:81/StudentService.svc");
            Student st = new Student{ Address = txtAddress.Text,
                                      Age = Convert.ToInt32(txtAge.Text),
                                      Name = txtName.Text,
                                      StudentClass = txtClass.Text,
                                      StudentID = txtID.Text};
            proxy.InsertStudentCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(proxy_InsertStudentCompleted);
            proxy.InsertStudentAsync(st);
        }

        void proxy_InsertStudentCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            if (e.Error == null)
                MessageBox.Show("Insertion Completed Successfully");
        }

  • Hit F5 and fill all the Student details and Pressing Insert Button will Insert the Student Record in Azure Table, on Successful return it will pop up a message dialog with Success full Insertion Message.

13 Insertion Completed Screen

  • To make sure that Entity has been inserted in Azure Table, Open SQLExpress Management Console.  This snap shows SQLExpress 2008 Management Console in the Tables section 02 tables are circled they contain data for every Azure Table Entity.

13 SQLExpressView

14 SQLExpressView

This Snap is the result of select Query on Last Table named TableRow, This is the Place where all the Entities are stored.

  • In this post we have covered
    • How to create Entity for Azure Table
    • Creating Context for Entity
    • Creating Repository for Bridging WCF Service and EntityContext
    • A Simple Silverlight Client to Insert Student Object.
  • what we will cover in next Post
    • Update, Delete and Retrieve Operations from Silverlight Client.

Download Complete code of Episode 2 from Here

5 comments:

  1. Many Thanx Inaam Bhai
    its realy a Gr8 effort and will help us too much in learning.

    Jamal Uddin

    ReplyDelete
  2. ahan working on 7 there.. are we :P
    Inam bahi.. I missed being with you man .. do something .. fly, transform come here we both can kick major ass in this shit :D
    ( provided you train me and explain me first whats going on) lol

    ReplyDelete
  3. Zabardast Effort.. I hope it will be completed soon.

    One tweak is required (Suggestion)

    These below lines should be in Loaded event. What you suggest? I think, it will create extra overhead.

    StudentServiceClient proxy = new StudentServiceClient();
    proxy.Endpoint.Address = new System.ServiceModel.EndpointAddress("http://localhost:81/StudentService.svc");
    proxy.InsertStudentCompleted += ...;

    ReplyDelete
  4. Thanks for your comments but don't you think Factory Method pattern would be a better approach for the sake of simplicity I avoided to use this pattern.

    ReplyDelete