Teknolojinin her geçen gün gelişmesi ve önünü alamadığımız bir internet çılgınlığıyla birlikte haliyle ortaya çok büyük bir veri yığınıda engellenemez olmakta. Bu gibi durumlarda sistemler üzerindeki trafik yoğunluğu, verinin depolanabilmesi ve gibi ihtiyaçlara yönelik esnek bir şekilde ölçeklenebilen bir yapıya ihtiyaç duyulmaktadır. Table storage kavramına değinmeden önce bu gibi ölçeklenebilir yapı olan NoSql yapısını bu şekilde özetlemek gerekti. NoSql alışa geldiğimiz SQL tablo ilişkisinin bir anlamda zıt yönünde gelişmektedir. NoSql üzerinde tablolarda herhangi bir relation bulunmamakta. Bu tamamen big data gibi karmaşık yapılara yönelik sunulmuş hızlı ve geliştirilmesi kolay bir yapı olarak ön plana çıkmaktadır. İşte bu noktada Microsoft Windows Azure platformu içerisinde bu gibi büyük datalar ve sistemler üzerinde gerçekleşen trafik yoğunluğunu ölçeklenebilir hale getirmek için Table Storage kavramını geliştirmiştir. Windows Azure Managemen Portal üzerinde storage account oluşturulduğu zaman “Dashboard” altında 3 adet storage adresi ön plana çıkacaktır ; Table Storage, Blob Storage ve Queue Storage.
Table storage yapısı REST API (Representational State Transfer) birlikteliğinin mümkün kılındığınıda belirtmek gerekmektedir. Bunun artısı, günümüz uygulamalarının çoğunluğu artık browser tabanlı gerçekleşmesinden kaynaklanmaktadır. REST API, client-server arasındaki ilişki sayesinde WCF servislerinden olabildiğince faydalanabileceğimiz anlamına gelmektedir. Böylece Browser tabanlı uygulamalar REST API alt yapısından faydalanakarak WCF servislerinizden beslenebilecektir. Table storage yapısı üzerinde bilinmesi gereken bir kaç temek kavram vardır.
Timestamp : Read only özelliğinden dolayı bu özellik üzerinde herhangi bir aksiyon gerçekleştiremiyoruz. Tamamen Windows Azure platformunun table storage yapısı için “optimistic concurrency possible” mümkün kılınması için hazırladığı bir özellikten ibarettir. Bu yüzden sadece kullanıyoruz ama herhangi bir işleme maruz bırakmıyoruz.
Partition Key : Bu özellik table storage mantığının temelini oluşturan özelliklerden birisidir. Tablolar üzerinde yoğunlaşan trafiğin dağıtılması görevi buradan sağlanır böylece tablo üzerindeki yük load-balancing işlemi uygulanır. Entity’lerinize ait olan kategori ve cluster’ları bu özellik üzerinden görebilir ve yönetebilirsiniz. Aynı Partition Key’lere ait olan entity’ler tek bir noktadan servis edilir ve yönetilir.
Row Key : Daha önceden belirlemiş olduğunuz Partition Key’ler için unique değer atanmasını sağlar. Bir anlamda klasik sql tablolarında belirlemiş olduğumuz Primary Key yapısının benzeri özelliği taşır.
Windows Azure Table storage örneğimiz için öncelikle, Windows Azure Management Portal üzerinden yeni bir storage account oluşturmamız gerekmekte.
oluşturduğumuz storage account içerisinde bizim projelerimizde kullanacağımız ve proje ile bağlantıyı kuracak olan Account Key ve Account Name sekmesini alttaki kısımda görebilirsiniz.
Şimdilik management portal üzerindeki işlemlerimiz tamamlandı. Sırada proje ile ilgili ayarlamaları yapmamız gerekmekte. Visual Studio üzerinden yeni bir WPF (Windows Presentation Foundation) projesi oluşturarak başlayabiliriz.
Projemizi oluşturduktan sonra Table Storage için gerekli SDK yüklemesini yapmalıyız. Bunun için References kısmında Nu-get package manager tıklanarak search kısmına windowsazure.storage yazarak gerekli sdk yüklenebilir.
Bu konfigürasyon tamamlandıktan sonra ihtiyacımız olan diğer dll’ler eklenerek proje için gerekli yapılandırmayı tamamlayabiliriz. System.Configruation ve System.Collection eklenerek referans tanımlama işlemini tamamlayabiliriz.
Projenin arayüzü için gerekli xaml kodları ve görüntüsü aşağıdaki şekilde olacaktır.
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="150"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Grid Grid.Column="0"> <ListBox Name="lst_Person" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Padding="10" SelectionChanged="lst_Person_SelectionChanged" /> </Grid> <Grid Grid.Column="1"> <Grid.RowDefinitions> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid Grid.Row="0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="80"/> <ColumnDefinition Width="150"/> <ColumnDefinition Width="80"/> <ColumnDefinition Width="150"/> </Grid.ColumnDefinitions> <Grid Grid.Column="0"> <Label Content="Departman" HorizontalAlignment="Left" VerticalAlignment="Top"/> </Grid> <Grid Grid.Column="1"> <ComboBox Name="cmb_Department" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5"/> </Grid> <Grid Grid.Column="2"> <Label Content="Title" HorizontalAlignment="Left" VerticalAlignment="Top"/> </Grid> <Grid Grid.Column="3"> <TextBox Name="txb_Title" HorizontalAlignment="Stretch" TextWrapping="NoWrap" VerticalAlignment="Stretch" Margin="5"/> </Grid> </Grid> <Grid Grid.Row="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="80"/> <ColumnDefinition Width="150"/> <ColumnDefinition Width="80"/> <ColumnDefinition Width="150"/> </Grid.ColumnDefinitions> <Grid Grid.Column="0"> <Label Content="First Name :" HorizontalAlignment="Left" VerticalAlignment="Top"/> </Grid> <Grid Grid.Column="1"> <TextBox Name="txb_FirstName" HorizontalAlignment="Stretch" TextWrapping="NoWrap" VerticalAlignment="Stretch" Margin="5"/> </Grid> <Grid Grid.Column="2"> <Label Content="Last Name :" HorizontalAlignment="Left" VerticalAlignment="Top"/> </Grid> <Grid Grid.Column="3"> <TextBox Name="txb_LastName" HorizontalAlignment="Stretch" TextWrapping="NoWrap" VerticalAlignment="Stretch" Margin="5"/> </Grid> </Grid> <Grid Grid.Row="2"> <Grid.ColumnDefinitions> <ColumnDefinition Width="255"/> <ColumnDefinition Width="255"/> </Grid.ColumnDefinitions> <Grid Grid.Column="0"> <Button Content="New" Name="btn_New" HorizontalAlignment="Right" VerticalAlignment="Center" Width="75" Margin="0,0,30,0" Click="btn_New_Click"/> </Grid> <Grid Grid.Column="1"> <Button Content="Save" Name="btn_Save" HorizontalAlignment="Left" VerticalAlignment="Center" Width="75" Margin="30,0,0,0" Click="btn_Save_Click"/> </Grid> </Grid> <Grid Grid.Row="3"> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="30"/> </Grid.RowDefinitions> <Grid Grid.Row="0"> <DataGrid Name="dtg_AllData" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,10,0,0"/> </Grid> <Grid Grid.Row="1"> <Button Content="Delete" Name="btn_Delete" HorizontalAlignment="Center" VerticalAlignment="Center" Width="75" Margin="30,0,0,0" Click="btn_Delete_Click" /> </Grid> </Grid> </Grid> </Grid>
İstediğimiz arayüz görüntüsünü sağladıktan sonra code-behind tarafına geçerek Table Storage üzerinde çeşitli operasyonları gerçekleştirebiliriz.
Daha önce windows azure tarafında oluşturduğumuz Storage Account içinde bulunan Account Name ve Account Key değerlerini projemiz içerisinde bulunan App.Config dosyası içerisinde eşleştirmesini sağlıyoruz.
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <appSettings> <add key="AccountName" value="Account Name Değeriniz"/> <add key="AccountKey" value="Account Key Değeriniz"/> </appSettings> </configuration>
Bunun için öncelikle oluşturmak istediğimiz tablo için bir class yazarak gerekli Attribute’leri yerleştiriyoruz.
public class EmployeeEntity : TableEntity { public EmployeeEntity(string department) { this.PartitionKey = department; this.RowKey = System.Guid.NewGuid().ToString(); } public EmployeeEntity() { } public string firstName { get; set; } public string lastName { get; set; } public string department {get;set;} public string title { get; set; } }
Yazının en başında belirttiğim gibi partition ve row key kısımlarını burada oluşturuyoruz. bu proje için oluşturduğum partition key department ve bu key için unique değer olarak aslında bizim primary key olarak tanımlayabileceğimiz kısım ise row key ve Guid mantığı ile bize bununla ilgili özel bir değer oluşturacak.
using Microsoft.WindowsAzure.Storage; using Microsoft.WindowsAzure.Storage.Auth; using Microsoft.WindowsAzure.Storage.Table; using System.Configuration; using System.Collections; namespace TableStorageSampleWPF { public partial class MainWindow : Window { CloudStorageAccount storageAccount; StorageCredentials storageCredentials; CloudTableClient tableClient; public MainWindow() { InitializeComponent(); Loaded += MainWindow_Loaded; } void MainWindow_Loaded(object sender, RoutedEventArgs e) { string accountName = ConfigurationManager.AppSettings.Get("AccountName"); string accountKey = ConfigurationManager.AppSettings.Get("AccountKey"); storageCredentials = new StorageCredentials(accountName, accountKey); storageAccount = new CloudStorageAccount(storageCredentials,true); tableClient = storageAccount.CreateCloudTableClient(); CreateTable(); GetTables(); string[] departments= new string[5] { "Matematik", "Bilgisayar", "Yazılım","Elektrik","Elektronik" }; foreach (var i in departments) { cmb_Department.Items.Add(i); } }
Yukarıda ilk başta tanımlamış olduğumuz;
CloudStorageAccount storageAccount; Windows Azure management portal üzerindeki oluşturulan storage account işlevinin proje tarafında eşitlenmesi ve doğrulanması görevini görür.
StorageCredentials storageCredentials; Azure managament portal üzerinde oluşturulan storage account ile otantikasyon yapılandırılmasını üstlenir ve bağlantı kurulumu üzerinde görev yapar.
CloudTableClient tableClient; İstemci tarafında sunulacak olan table servisini mantıksal olarak oluşturmak için kullanılır. Bu yapı ile table servisinin yapılandırması ve execute edilmesi sağlanmaktadır.
Main Window Loaded Event içerisinde göreceğiniz CreateTable() metodu için yapılması gereken işlem, eğer storage account’umuz içerisinde herhangi bir tablo varmıdır yokmudur bunun kontrolü ile eğer yoksa yeni bir tablo oluşturmaktır.
private void CreateTable() { CloudTable table = tableClient.GetTableReference("Employee"); table.CreateIfNotExists(); }
Yukarıdaki gibi Employee adında bir tablo oluşturdum ancak bunu CreateIfNotExists kullanarak gerçekleştirdim ve böylece bu değere sahip bir tablo yoksa yeniden oluşturulmasını tetiklemiş oldum.
Oluşturduğumuz tabloları proje arayüzünde bulunan ListBox içerisinde görünmesini sağlamamız gerekmekte bunun için tekrar yukarıdaki Main Window Loaded Event içerisinde görebileceğiniz GetTables() metodundan yararlanacağız.
private void GetTables() { Cursor = Cursors.Wait; lst_Person.Items.Clear(); IEnumerable<CloudTable> tableList = tableClient.ListTables(); foreach (CloudTable tab in tableList) { lst_Person.Items.Add(tab); } Cursor = Cursors.Arrow; }
Oluşturduğumuz tablo isimlerini ListBox içerisinde gördük şimdi bu tabloya ait verilerin DataGrid içerisinde gözükmesi için ListBox SelectionChanged Event’i tetikleyerek gerekli bilgileri çekebiliriz.
private void lst_Person_SelectionChanged(object sender, SelectionChangedEventArgs e) { CloudTable table = tableClient.GetTableReference(lst_Person.SelectedItem.ToString()); TableQuery<EmployeeEntity> query= new TableQuery<EmployeeEntity>(); IEnumerable<EmployeeEntity> list=table.ExecuteQuery(query); dtg_AllData.ItemsSource = list.ToList(); }
Tablomuza yeni kayıt girilmesini sağlamak için Save butonu altında yeni bir Insert Operation tanımlamamız gerekmektedir. Insert operation işlemi aşağıda göreceğiniz gibi oldukça kısa bir kod parçacığı ile büyük kolaylık sağlamaktadır.
private void btn_Save_Click(object sender, RoutedEventArgs e) { CloudTable table = tableClient.GetTableReference(lst_Person.SelectedItem.ToString()); EmployeeEntity emp = new EmployeeEntity(cmb_Department.SelectedItem.ToString()); emp.firstName = txb_FirstName.Text; emp.lastName = txb_LastName.Text; emp.title = txb_Title.Text; TableOperation insert = TableOperation.Insert(emp); table.Execute(insert); MessageBox.Show("Employee Added"); }
Kayıtlı veriyi silmek için yine insert operation benzeri kolay ve kısa bir adımda delete operation kullanarak ilerleyebiliriz.
private void btn_Delete_Click(object sender, RoutedEventArgs e) { CloudTable table = tableClient.GetTableReference("people"); TableOperation retrieve = TableOperation.Retrieve<EmployeeEntity> ("matematik","902ba198-4da6-4ade-ada3-5fb162b93b65"); TableResult result=table.Execute(retrieve); EmployeeEntity emp = (EmployeeEntity)result.Result; if (emp!=null) { TableOperation delete = TableOperation.Delete(emp); table.Execute(delete); MessageBox.Show("item deleted"); } }
yukarıdaki delete operation işlemi içerisinde belirlediğimiz row key ve partition key mantığının anlaşılabilmesi için silinmek istenen değerin bu iki key üzerinden silinebileceğini göstermek istedim. (“matematik”,”902ba198-4da6-4ade-ada3-5fb162b93b65″) key value’ları ile silme işlemini gerçekleştirebiliriz. ancak datagrid üzerinden seçilen satıra ait yine bu değerleri yakalayarakta silmeyi gerçekleştirebiliriz.
Visual Studio üzerindeki Server Explorer kısmında Windows azure storage alanına sağ tık ve yeni storage account ekle diyerek oluşturduğunuz table storage accountunu bağlayarak tablonuzu bu kısımdan görebilir ve yönetebilirsiniz.