Azure Storage Services – Part 7:.NET ile Storage Service Kullanımı

Makale serimizin 7. bölümünde sözü Fatih Doğan’a bırakacağız. Kendisi bu ana kadar öğrendiğimiz Azure storage hizmetlerini .NET ile nasıl ayrıntılı şekilde yönetebileceğimizi anlatıyor olacak.

———————————————–

Storage üzerinde yapabileceğiniz bütün işlemlerin programlanabilir olması, düzenli olarak yapmanız gereken işleri küçük bir uygulama yazarak otomatize etmenize olanak sağlıyor. Hatta bütün bu işlemler RESTful bir API üzerinden yapıldığı için HTTP isteği gönderebilen bütün dillerden storage management yapabiliyorsunuz. Tabi HTTP headerlar ve bodylerle uğraşmak istemiyorsanız, kendi programlama dilinize ait, bu işlemleri kolayca yapabileceğiniz wrapperlar bulmak zorunda kalıyorsunuz – yazdığınız dile ait storage wrapper bulamıyorsanız, GitHub sizi ve yazacağınız wrapperı bekliyor.

Tabi konu .NET olunca ve Storage’da bir Microsoft hizmeti olunca, aradığımız wrapperın da Microsoft tarafından temin edilmesi pek şaşırtıcı olmuyor. Böyle dediğime bakmayın, Microsoft tarafından temin edilen wrapperlar arasında sadece .NET yok ; Node.js, Java, PHP, Ruby ve Python da var.
Biz ise makale gereği .NET ile, daha açık konuşmak gerekirse .NET dillerinden biri olan C# ile Storage hesabımız üzerinde yapabileceğimiz işlemleri inceleyeceğiz.

0. Ön Hazırlık

Azure hesabınıza giriş yaparak, üzerinde çalışacağınız Storage hesabınızı seçin ve “Manage Account Key” sekmesine geçiş yapın. “Storage Account Name” ve “Primary Access Key” bölümlerini ise bir yere not edin. Ayrıca NuGet’den “WindowsAzure.Storage” araması yaparak gerekli wrapperı projenize referans etmeniz gerek. Referans etmeniz gereken bir diğer assembly ise “System.Configuration”.

Tabi her şey referans eklemekle bitmiyor. Bir yere not ettiğiniz Storage Account Name ve Primary Access Key’i kullanarak app.config – ya da bir Web uygulaması yazıyorsanız web.config içerisine aşağıdaki formata uyacak şekilde gerekli eklemeleri yapıyoruz.

<connectionStrings>
  <add name="StorageConnectionString" connectionString="DefaultEndpointsProtocol=https;AccountName=StorageAccountName;AccountKey=PrimaryAccessKey" />
</connectionStrings>

Ekleme sonrası ulaşacağınız sonuç buna benzer bir tanımlama olmalı;

<connectionStrings>
  <add name="StorageConnectionString"
       connectionString="DefaultEndpointsProtocol=https;AccountName=storagexpress;AccountKey=u8ZH06jlpIv1Qvrqskb5AkC2NxpGM365CFLo3H8VQzonVZc8sPjP26yKrFYz6V8mHKdtOFu41y+UyESCxkrtiw==" />
</connectionStrings>

Projeye gerekli referansları ekledikten sonra proje içerisinde kullanılacak using statementlarını ekliyoruz.

using System.Configuration;

using Microsoft.WindowsAzure.Storage;

using Microsoft.WindowsAzure.Storage.Auth;

using Microsoft.WindowsAzure.Storage.Blob;

using Microsoft.WindowsAzure.Storage.Table;

using Microsoft.WindowsAzure.Storage.Queue;

Storage hesabına erişim sağlamak için yeni bir “CloudStorageAccount” nesnesi oluşturuyoruz.

CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString);

1. Blob Storage

Önceden oluşturduğumuz “CloudStorageAccount” yardımıyla blob storage işlemlerini yapacağımız “CloudBlobClient” nesnesini oluşturuyoruz.

CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

1.1 Container Oluşturma

Sonrasında ise Blob Storage içerisindeki işlem yapacağımız containerın referansına ihtiyacımız var. Bu referans sayesinde container üzerinde işlemler yapabileceğiz. Bu referansı almanın ise birkaç yolu var. Birincisi “CloudBlobClient” ögesine ait “ListContainers” metodu ile blob storageınız içerisindeki bütün containerların bir referansını alabilirsiniz. İkincisi ise, işlem yapmak istediğiniz containerın adını biliyorsanız ya da o isimde bir container oluşturmak istiyorsanız yine “CloudBlobClient” ögesine ait “GetContainerReference” adlı metodu kullanıyoruz;

CloudBlobContainer blobContainer = blobClient.GetContainerReference("mystorage");

if (blobContainer.Exists())
{
    blobContainer.Create();
    blobContainer.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
}

“SetPermissions” metodu ile containerın permission ayarlamalarını yapabilirsiniz. PublicAccess tarafında ise seçebileceğiniz üç adet seçenek var. Birincisi, örnekte de görebileceğiniz gibi “Blob”. Bu seçeneği kullanarak, container içerisindeki bloblara erişimin herkese açık olduğunu, ama containerın içeriğinin listelenemeyeceğini belirtiyorsunuz. İkincisi ise “Container” ki bu seçenek containerı herkese açık hale getiriyor. Üçüncü ve son olarak “Off” ile de sadece yetkisi olan kişilerin containerınıza erişim sağlayabileceğini belirtiyorsunuz.

1.2 Container’a Dosya Yükleme

CloudBlockBlob blockBlob = blobContainer.GetBlockBlobReference("myblob");

using (System.IO.MemoryStream stream = new System.IO.MemoryStream(new System.Net.WebClient().DownloadData(new Uri("http://www.bing.com/robots.txt"))))
    blockBlob.UploadFromStream(stream);

Yukarıdaki örnekte de görebileceğimiz gibi ilk olarak işlem yapacağımız blobun referansını elde ediyoruz. Sonrasında ise bir dosyanın içeriğini Stream olarak alıyoruz ve “UploadFromStream” metodunu kullanarak blob üzerine yükleme işlemini başlatıyoruz. Örnekte ise Bing’e ait robots.txt dosyasının içeriğini stream olarak alıyoruz ve “UploadFromStream” metodunu kullanarak yükleme işlemini başlatıyoruz.

1.3 Container İçerisindeki Blobları Listeleme

 foreach (IListBlobItem blobItem in blobContainer.ListBlobs())
 {
     if (blobItem is CloudBlockBlob)
     {
         CloudBlockBlob cloudBlockBlob = (CloudBlockBlob)blobItem;
         Console.WriteLine("CloudBlockBlob: {{ Properties.Length : {0} , Uri : {1} }}", cloudBlockBlob.Properties.Length, cloudBlockBlob.Uri);
     }
     else if (blobItem is CloudPageBlob)
     {
         CloudPageBlob cloudPageBlob = (CloudPageBlob)blobItem;
         Console.WriteLine("CloudPageBlob: {{ Properties.Length : {0} , Uri : {1} }}", cloudPageBlob.Properties.Length, cloudPageBlob.Uri);
     }
     else if (blobItem is CloudBlobDirectory)
         Console.WriteLine("CloudBlobDirectory : {{ Uri : {0} }}", blobItem.Uri);
 }

“ListBlobs” metodunu kullanarak container içerisindeki bütün blobları listeleyebiliyoruz. Yukarıdaki örnekte ise gelebilecek blobları tiplerine göre ayırıyoruz ve ona göre çıktı sağlıyoruz.

1.4 Container İçerisindeki Bir Blobu İndirme

 blockBlob.DownloadToFile("<Dosya Yolu>", FileMode.OpenOrCreate);

Örnekte de görebileceğiniz gibi, “CloudBlockBlob” ögesinin “DownloadToFile” metodu ile blobunuzu indirebilirsiniz. Onun dışında, öge içerisindeki “DownloadTo” ile başlayan metodlarla, bloblarınızı farklı şekillerde de indirebilirsiniz.

1.5 Bir Blobu Silme

 blockBlob.DeleteIfExist();

“CloudBlockBlob” içerisindeki “Delete” metodu ile referansını aldığınız blobu silebilirsiniz. “DeleteIfExists” metodunu da kullanabilirsiniz. Arasındaki fark ise, blobun hali hazırda var olup olmadığını kontrol ederek işlem yapmasıdır. Eğer blobu bulabilirse siler.

2. Table Storage

 CloudTableClient tableClient = storageAccount.CreateCloudTableClient();

Blob Storage örneğinde olduğu gibi, “CloudStorageAccount” ögesini kullanarak bir “CloudTableClient” oluşturuyoruz.

2.1 Tablo Oluşturma

CloudTable table = tableClient.GetTableReference("users");

table.CreateIfNotExists();

Table client oluşturulduktan sonra işlem yapmak istediğimiz tablonun referansını alıyoruz. Bu örnekte “users” adlı bir tablo ile işlem yapacağız. Tablonun var olup olmadığıyla ilgili bir bilginiz yoksa eğer “CreateIfNotExists” adlı metodu kullanabilirsiniz. Bu metod, eğer tablo yoksa oluşturacaktır.

2.2 Insert işlemleri

Insert işlemlerinden bahsetmeden önce, neleri insert edebileceğimizden bahsedelim. İlk önce nasıl bir yapı kullanılması gerektiğini örnekle gösterdikten sonra üzerinde açıklama yapacağız ;

public class UserEntity : TableEntity

{

public UserEntity() { }

public UserEntity(string name, string surname)

{

PartitionKey = name;

RowKey = surname;

}

public string Email { get; set; }

}

Burada bir kaç şey dikkatimizi çekiyor. Birincisi “TableEntity” sınıfından alınan kalıtım. Bunun nedeni ise, table storage altyapısı gereği, ögelerinizin bir kurala uyması gerektiği. TableEntity kalıtımı ile birlikte sınıfınıza “RowKey” ve “PartitionKey” adlı elemanlar ekleniyor. Bu ögeler sayesinde tablonuzdaki bütün girdilerin benzersiz (a.k.a. unique) olması sağlanıyor.

Sonrasında ise ögemizi oluşturuyoruz ve insert işlemini gerçekleştiriyoruz ;

 UserEntity user = new UserEntity("Jack", "Sparrow")
 {
     Email = "jack.sparrow@outlook.com"
 };

 TableOperation insertOperation = TableOperation.Insert(user);

 table.Execute(insertOperation);

Insert,Update,Delete ve Merge işlemlerini yapmak için “TableOperation” oluşturmak gerekiyor. TableOperation ögesinin içerisinde bu işlemler için tanımlanmış statik metodlar mevcut.

TableOperation ögesi oluşturulduktan sonra, bu işlemin tabloda çalıştırılması için “CloudTable” ögesinin “Execute” metodunu çağırmamız gerekiyor.

2.3 Batch Insert İşlemleri

TableBatchOperation batchOperation = new TableBatchOperation();

UserEntity user1 = new UserEntity(“John”, “Coss”)

{

Email = “john.coss@outlook.com”

};

UserEntity user2 = new UserEntity(“John”, “Bell”)

{

Email = “john.bell@outlook.com”

};

batchOperation.Insert(user1);

batchOperation.Insert(user2);

table.ExecuteBatch(batchOperation);

Birden fazla işlem yapacaksanız, her bir işlemin sunucuya gidip gelmesini beklemektense, yapacağınız bütün işlemlerin tek bir gidişte gönderilmesini isteyebilirsiniz. Bu işlemler bir “yığın” halinde gönderildiği için ise “Batch” adı veriliyor.

Öncelikle bir “TableBatchOperation” oluşturuyoruz. Batch olarak göndermek istediğiniz bütün işlemleri bu öge üzerinde tanımlayacağız. Bu örnekte ise iki adet “UserEntity” ögesi oluşturduk “TableBatchOperation” ögesine insert işlemlerini gerçekleştirdik. Sonrasında ise “CloudTable” ögesinin “ExecuteBatch” metoduna, hazırladığımız “TableBatchOperation” ögesini parametre olarak gönderiyoruz. Yalnız atlamamanız gereken bir ayrıntı var ; Batch işlem yapmak istiyorsanız, batch içerisinde kullanacağınız ögelerin PartitionKeyleri aynı olmalı. Bu yüzden örnek içerisinde kullanılan iki UserEntitysinde kullanılan isim “John” olarak tanımlı.

2.4 Query Oluşturma

 TableQuery<UserEntity> query = new TableQuery<UserEntity>()
                                    .Where(
                                        TableQuery.GenerateFilterCondition(
                                            propertyName: "PartitionKey",
                                            operation: QueryComparisons.Equal,
                                            givenValue: "John"
                                        )
                                    );

 foreach (UserEntity item in table.ExecuteQuery(query))
 {
     Console.WriteLine("UserEntity : {");
     Console.WriteLine("    PartitionKey : {0}", item.PartitionKey);
     Console.WriteLine("    RowKey : {0}", item.RowKey);
     Console.WriteLine("    Email : {0}", item.Email);
     Console.WriteLine("}");
 }

Tablonuza sorgu çekmek, Table storageda biraz farklı. Sanırım sınıf içerisindeki metodların, Linq ile birlikte gelen extension metodlar ile karışmasını istemediklerinden dolayı böyle bir yola başvurmuşlar. Yukarıdaki karışıklığı biraz açmak gerekirse ; Öncelikle bir “TableQuery” oluşturuyoruz. Bu sınıf generic bir sınıf olduğundan dolayı işlem yapmak istediğimiz entitynin tipini verebiliyoruz. Sonrasında ise içerisine string olarak bir parametre alan “Where” metodumuz var. El ile sorgu yazma alışkanlığımız olmadığından dolayı (!) sorgu oluşturmak için sınıf içerisinde birkaç static metod tanımlı. “GenerateFilterCondition” metodu içerisine, sorgulanacak propertynin adı (propertyName), eşitliği belirleyecek operasyon (queryOperation) ve karşılaştırılacak değeri (givenValue) parametre olarak alıyor. “QueryComparisons” adlı sınıf içerisinde ise eşitliği belirleyebilecek operasyonlar tanımlı. Biz ise bu örnekte “Equal” ögesini kullanıyoruz. Böylece “PartitionKey”lerin “John”a “eşit” olduğu entityleri çekebiliyoruz.

2.5 Sadece Bir Girdiyi Getirme

 TableOperation retrieve = TableOperation.Retrieve<UserEntity>("John", "Bell");
 TableResult tableResult = table.Execute(retrieve);
 UserEntity retrievedUser = (UserEntity)tableResult.Result;

“TableOperation” sınıfı içerisindeki “Retrieve” metoduna, parametre olarak “PartitionKey” ve “RowKey” göndererek tekil girdileri getirebilirsiniz. Hazırlanan sorguyu “Execute” ettiğinizde gelecek “Result” ise sorgunuzun sonunda dönen sonuç olacağından, sonucu cast ederek girdiye ulaşabilirsiniz.

2.6 Bir Girdiyi Update Etme

 retrievedUser.Email = "johnBell@outlook.com";
 TableOperation replaceOperation = TableOperation.Replace(retrievedUser);
 table.Execute(replaceOperation);

Örnek olması için bir önceki başlıkta getirdiğim ögenin “Email” özelliğine başka bir değer atıyorum. Sonrasında ise “TableOperation” sınıfı içerisindeki “Replace” metoduna, update edilmesini istediğimiz entityi parametre olarak yolluyoruz. Sonrasında ise hazırladığımız “TableOperation” ögesini “Execute” ediyoruz.

2.7 Girdiyi Update ya da Insert Etme

retrievedUser.Email = "john.bell@outlook.com";
TableOperation replaceOrInsertOperation = TableOperation.InsertOrReplace(retrievedUser);
table.Execute(replaceOrInsertOperation);

Bazen elinizdeki ögenin tablonuzda olup olmadığını bilemiyorsunuz. Update işlemi yapmaya çalışırsanız ve tabloda öyle bir öge yoksa hata alırsınız. Insert etmeye çalışırsanız, tabloda aynı PartitionKey ve RowKeye sahip bir öge varsa hata alırsınız. İşte o yüzden, emin olmadığınız durumlarda “InsertOrReplace” metodunu kullanarak “TableOperation” oluşturabilirsiniz. Sonrasında ise yine aynı şekilde, oluşturulan “TableOperation” ögesi “Execute” ediliyor.

2.8 Dynamic Sorgu (Projection)

Sorgu çekmeye çalıştığınız tablonun yapısını bilmiyorsanız, ya da elinizde bu tabloya ait bir entity sınıfı yoksa, bu sorgu türünü kullanabilirsiniz. Nasıl kullanıldığını bir örnek ile açıklayalım ;

TableQuery<DynamicTableEntity> projectionQuery = new TableQuery<DynamicTableEntity>().Select(new[] { "Email" });
EntityResolver<string> resolver = (pk, rk, ts, prop, etag) => prop.Keys.Contains("Email") ? prop["Email"].StringValue : null;

foreach (string email in table.ExecuteQuery(projectionQuery, resolver))
    Console.WriteLine(email);

Öncelikle önceki örneklerde oluşturduğumuz gibi bir “TableQuery” oluşturuyoruz. Yalnız bu sefer entity tipi olarak “DynamicTableEntity” kullanıyoruz. “Select” metodu ile de getirmek istediğiniz kolonları belirtiyoruz. Bu örnekte ise “EMail” kolonunu çekiyoruz.

Sonrasında ise “EntityResolver” sınıfını kullanarak, sorgu sırasında getirilecek sonuçların bize göre anlamlandırılmasını sağlıyoruz. Generic bir sınıf olduğundan dolayı, getirmek istediğimiz değerin tipini veriyoruz. Bu örneğe göre, “Email” özelliğini “string” değer olarak tanımladığımızdan, tipini “string” olarak belirliyoruz. Eğer birden fazla kolon belirttiyseniz bu tipe dynamic ya da belirttiğiniz ögeleri bulunduran bir sınıfın tipini verebilirsiniz.

“ExecuteQuery” metoduna, hazırlanan sorguyu ve sorgudan geriye dönecek sonuçları resolve edecek “EntityResolver” ögesini parametre olarak gönderiyoruz.

2.9 Girdi Silme

TableOperation deleteOperation = TableOperation.Delete(retrievedUser);
table.Execute(deleteOperation);

Silmek istediğiniz ögeyi, “TableOperation” sınıfının “Delete” metoduna parametre olarak göndererek, geriye dönen “TableOpration” ögesini “Execute” edin.

2.10 Tablo Silme

table.DeleteIfExists();

“CloudTable” ögesinin “DeleteIfExists” metodu ile, aldığınız tablo referansına ait bir tablo var ise silebilirsiniz.

3. Queue Storage

Table ve Blob Storage başlıklarında da yaptığımız gibi, Queue işlemleri yapmak istiyorsak bir client oluşturmamız gerekiyor.

CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

3.1 Queue Oluşturma

Bir queue ile işlem yapmak istiyorsanız, işlem yapmak istediğiniz queuenin referansını almanız gerekiyor. Sonrasında ise bu referansa ait bir queue yoksa oluşturabilirsiniz.

CloudQueue queue = queueClient.GetQueueReference("myqueue");

queue.CreateIfNotExists();

Bu örnekte ise “myqueue” adlı bir queuenin referansını alıyoruz. Aldığımız referans sonrasında, eğer bu isimde bir queue yoksa oluşturulması için “CreateIfNotExists” metodunu çağırıyoruz.

3.2 Queueya Mesaj Ekleme

 for (int i = 0; i < 5; i++)
 {
     CloudQueueMessage message = new CloudQueueMessage("Merhaba Dünya !!!");
     queue.AddMessage(message);
 }

Yukarıdaki örnekte 5 adet mesaj oluşturuyoruz ve bunları queueya ekliyoruz. Queueya eklemek için kullanılacak mesajı “CloudQueueMessage” sınıfını kullanarak tanımlıyoruz. Sonrasında ise “CloudQueue” ögesinin “AddMessage” metodunu kullanarak mesajımızı gönderiyoruz.

3.3 Queueya Göz Atma

Queuedaki bir mesajı queuedan silmeden getirmek için Peek özelliğini kullanabilirsiniz.

CloudQueueMessage peekedMessage = queue.PeekMessage();
Console.WriteLine(peekedMessage.AsString);

“CloudQueue” ögesinin “PeekMessage” metodu ile mesajı getirebilirsiniz. Örnekte ise aldığımız mesajı konsolda yazdırıyoruz.

3.4 Queueya Eklenen Mesajı Düzenleme

CloudQueueMessage updatedMessage = queue.GetMessage();
updatedMessage.SetMessageContent("Hello World !!!");
queue.UpdateMessage(updatedMessage, TimeSpan.FromSeconds(0), MessageUpdateFields.Content | MessageUpdateFields.Visibility);

İlk olarak mesajı “GetMessage” metodu ile çekiyoruz. Sonrasında ise, “CloudQueueMessage” ögesinin “SetMessageContent” metodunu kullanarak içerik değişikliğini yapıyoruz. İçerik değişikliğini yaptıktan sonra ise, “CloudQueue” ögesinin “UpdateMessage” metodunu kullanarak mesajı güncelliyoruz. Bu metod ise içerisine sırasıyla, güncellenecek mesaj, bu değişiklik etkili olana kadar geçecek süre ( ki bu örneğe göre değişikliğin hemen yapılması gerektiğini söylüyoruz ) ve son olarak mesajın güncellenen bölümlerini belirtiyoruz (örneğe göre mesajın içeriğinde ve görünürlüğünde değişiklik yapıldığı için “Content” ve “Visibility” ögelerini yolluyoruz).

3.5 Queuedan Mesajı Silme

 CloudQueueMessage deletedMessage = queue.GetMessage();
 queue.DeleteMessage(deletedMessage);

“CloudQueue” sınıfı içerisindeki “DeleteMessage” metodu ile mesajları silebilirsiniz.

3.6 Queuedaki Mesaj Sayısını Getirme

 queue.FetchAttributes();

 Console.WriteLine("{0} adet mesaj var.", queue.ApproximateMessageCount);

En güncel mesaj sayısını almak istiyorsanız, öncelikle “CloudQueue” ögesinin “FetchAttributes” metodunu kullanarak, queuenizin güncel bilgileri almasını sağlamanız gerekiyor. Sonrasında ise “CloudQueue” ögesinin “ApproximateMessageCount” özelliği, size asıl mesaj sayısına yakın bir sayı getirecektir. “FetchAttributes” metodu çağırıldıktan sonra queue üzerinde yapılabilecek mesaj ekleme ya da kaldırma işlemleri, bu propertyi güncel olmaktan çıkaracaktır. Bundan dolayı property “ApproximateMessageCount” olarak adlandırılmıştır.

3.7 Queuedaki Mesajları Getirme

 Console.WriteLine("Mesajlar : {");
 foreach (CloudQueueMessage item in queue.GetMessages(5, TimeSpan.FromMinutes(5)))
 {
     Console.WriteLine("    {0}", item.AsString);
 }
 Console.WriteLine("}");

Birden fazla mesajı getirmek isterseniz “CloudQueue” ögesinin “GetMessages” metodunu kullanabilirsiniz. Örnekte “GetMessages” metoduna iki adet parametre gönderilmiş. Bu parametreler sırasıyla, getirilmek istenen mesaj sayısı ve bu mesajların ne kadar bir süre sonra queuedan silineceğini gösteriyor. Yani örneğe göre beş adet mesaj çağırılıyor ve bu mesajlar 5 dakika sonra silinecek.

3.7 Queueyu Silme

queue.DeleteIfExists();

“CloudQueue” ögesinin “DeleteIfExists” metodunu kullanarak, eğer referansını aldığınız queue var ise silebilirsiniz.

Bu makalede Blob, Table ve Queue Storage üzerinde, C# kullanarak yapabileceğimiz işlemleri inceledik. Tabi kütüphaneyi de ayrıca incelemenizi tavsiye ederim. Örneklerde kullandığımız kodlara aşağıdaki linkten ulaşabilirsiniz.

http://1drv.ms/14q1jOE

One thought on “Azure Storage Services – Part 7:.NET ile Storage Service Kullanımı

Leave a Reply

Your email address will not be published. Required fields are marked *

67 − = 65