Using the CloudStorage.API: The Blob Storage

We continue our blog series on using the Cloud Storage API (CloudStorage.API) by showing how it interacts with Cloud Blob Storage. As in the previous post we will use a short example focusing on basic usage.

The implementation we use for the API is developed against Azure but the API should be reusable for any type of Cloud Storage. While doing this our main goals are to:

  • Enable testability
  • Abstract away storage
  • Create an extensible and easy to evolve application that supports good developments practices

This is the second of three posts showing functionality of the API. The first explained how to use the Message Queue and the last one will explain the Entity Storage.

Here are other posts in this series:

Note: We are about to publish all of these samples on Azure Contrib. (Soonish…)

Overview

As in the previous example with Message Queues we will use a really simple application as a proof of concept. This time it’s a FileUpload control, a Repeater and a Button.

blobsampleapp

So, this application takes a file, uploads it to the Blob Storage and then a link to the file in the asp Repeater.

Pushing forward with TDD

As always we aim for testability and therefore we want to move as much of the logic, as possible, away from the ASP page into a class that we can easily test. For the Queue Service we created a controller class which took an interface from the Cloud Storage API as a parameter. The controller class then handled all the logic for the corresponding ASP page. We now had a decoupled and testable class. We’ll do the same thing here :-)

First we write the test for retrieving the blobs from the Blob Storage.

IBlobContainerDataContext _blobDataContext;
BlobSampleController _blobSampleController;

[TestMethod]
public void GetListOfBlobs_FindsThreeBlobs_ShouldReturnThreeBlobs()
{
    //Arrange
    var list = new List<IBlobInfo>
    {
        new BlobInfo("1"), new BlobInfo("2"), new BlobInfo("3")
    };
    var listBlobResponse = MockRepository.GenerateStub<IListBlobResponse>();
    listBlobResponse.Stub(x => x.Items)
.Return(list); _blobDataContext.Stub(x => x.ListBlobs(string.Empty, false)) .IgnoreArguments() .Return(listBlobResponse); //Act var listOfBlobs = _blobSampleController.GetListOfBlobs(); //Assert Assert.AreEqual(3, listOfBlobs.Count()); }

Now, this test may need some explaining. When listing blobs we will call the ListBlobs method located on IBlobContainerDataContext. This will give us an IListBlobResponse. The reason for not directly returning a list of blobs is simply because the REST API doesn’t. The IListBlobResponse contains an IEnumerable<IBlobInfo>. An IBlobInfo is everything that a Blob is except the content. The blobs Name, Uri, Metadata etc. This means that ListBlobs won’t go a fetch huge amount of data only to show the blobs in a list. Although IBlobInfo doesn’t contain the content it does contain an Uri for fetching the content.

Now, let’s implement .GetListOfBlobs(). When adding a Blob you can choose to add it with a prefix. This enables you to filter the response that you get when invoking .ListBlobs(string prefix, bool combineCommonPrefixes). However, in our example we won’t add the files with a prefix, simply because we don’t need to. We will list all the files regardless of their prefix and therefore we’ll send in an empty string as the prefix.

public IEnumerable<IBlobInfo> GetListOfBlobs()
{
    var listBlobResponse = _blobDataContext.ListBlobs(string.Empty, false);
    return listBlobResponse.Items;
}

The PutBlob method is really similar to that of PutMessage in the previous post, and so are the tests.

public void PutBlob(bool hasFile, string fileName, Stream fileContent)
{
    if (hasFile == false) return;

    _blobDataContext.PutBlob(new BlobInfo(fileName), fileContent);
}

To tie it all together

The code behind is intentionally simple containing only DataBinding and forwarding of information to the controller.

protected void Page_Load(object sender, EventArgs e)
{
    var blobContainerDataContext = CloudStorageDataContextFactory.GetBlobContainer("blobContainer");
    _blobSampleController = new BlobSampleController(blobContainerDataContext);

    DataBindBlobRepeater();
}

protected void UploadButton_OnClick(object sender, EventArgs e)
{
    _blobSampleController.PutBlob(BlobFileUpload.HasFile, BlobFileUpload.FileName, BlobFileUpload.FileContent);
}

private void DataBindBlobRepeater()
{
    BlobRepeater.DataSource = _blobSampleController.GetListOfBlobs();
    BlobRepeater.DataBind();
}

We use the same factory that we used when we worked with the Queue but this time we have the factory create an IBlobContainerDataContext for us instead.

And there it is. We showed how we in a simple way can upload files and then link to them. The files will be stored (probably) in Windows Azure Cloud Blob Storage. But we can’t be too sure. What if the API returns some other resource that implements our API? We could work against a local database, against another Cloud Storage, against a company server farm or against a mock. Honestly; who cares? All our app cares about is the service; to be able to store and get blobs. There they go in the mean time is not really important at all! ;~)

/P

P.S.: Note from M. This is the second time Peter is posting on my blog! (All I did was review and act picky!) Thank you man – good work! We are working right now on the third sample Using the CloudStorage.API: The Entity Storage.

Digg This

posted @ Wednesday, August 19, 2009 9:19 AM

Print

Comments on this entry:

# Extensible Windows Azure projects using MEF

Left by Techie.notepad at 9/7/2009 12:08 AM
Gravatar
Extensible Windows Azure projects using MEF

# 

Left by dotway at 9/7/2009 12:01 PM
Gravatar
Extensible Windows Azure projects using MEF

# 

Left by dotway at 10/7/2009 11:27 PM
Gravatar
Extensible Windows Azure projects using MEF!

# 

Left by dotway at 10/7/2009 11:31 PM
Gravatar
Extensible Windows Azure projects using MEF

Your comment:



 (will not be displayed)


 
 
 
Please add 5 and 8 and type the answer here:
 

Live Comment Preview: