【IT168 专稿】Windows Azure Table提供了一种以结构化存储数据的方法,Windows Azure Table不像传统的关系型数据库表(如SQL Server里的Table),也不像是云端的关系型数据库Windows Azure SQL中的关系型数据库一样存储具有关系型的数据。Windows Azure Table存储的是能够易于扩展的数据实体和属性。
构成Windows Azure Table的是数据实体,数据实体则由属性组成。经过扩充,Windows Azure Table可容纳数以亿计的个体单位和兆位元组 (TB) 大小的资料,并可分割储存到数千部伺服器上。在Windows Azure Table中,不仅可以进行单一个体单位的创建、修改和删除,也可执行涵盖整个Windows Azure Table的丰富查询。
对开发者来说,使用Table Service API,Windows Azure SDK与.Net平台提供的ADO.NET Data Services,能够使用.Net开发者所熟悉的界面进行Windows Azure Table的程序设计与开发。在本文中,将向大家介绍Windows Azure Table的架构,在此基础上通过一个具体案例来介绍如何使用Windows Azure Table来进行开发。
Windows Azure Table架构
(1)Windows Azure Table应用架构
同Windows Azure Blob、Windows Azure Queue架构介绍一样,对Windows Azure Table架构的介绍也同样分成两部分来介绍,第一部分是对Windows Azure Table应用程序架构介绍;第二部分介绍开发环境中的Windows Azure Table数据库对象。
Windows Azure Table从应用程序上来说,架构如图1所示:

图1 Windows Azure Table架构
由图1我们可以看到,Windows Azure Table的架构有一个层级关系,在最顶层是Table Service REST API,这个API为开发者提供了访问Table服务的应用程序访问接口,Table Service API在MSDN的地址是:
http://msdn.microsoft.com/en-us/library/dd179423.aspx
感兴趣的读者可以去了解更多信息,通过API,可以查询、创建、删除Table,也可以对Table的实体对象进程查询、创建、更新、合并和删除操作。
紧随Table Service API的是Windows Azure Storage Account,Windows Azure存储服务的存储账户,这个存储账户的地址是:
在存储账户下,存储的就是我们的Table了,一个Table的地址是:
在每个Table里,存储了类似SQL Table里的记录,这些记录叫做Table Row,每个Table Row记录了Table名称以及实体数据,由上图我们也可以看出来。在实体中,每个实体都有由应用程序所定义的属性,可以说Table Row就是一个个实体对象。
(2)Windows Azure Table数据模型
在本地开发的环境中,我们使用了安装在本地的SQL Express。要想了解Blob的存储结构,首先连接到数据库上看看。
使用SSMS连接本地的SQL Express数据库,如图2所示:

图2 连接本地SQL Express
在这里使用的SQL Express版本是SQL Express 2008,连接上本地SQL Express后,我们可以看到开发环境的存储数据库如图3所示:

图3 开发环境存储表
由图3中的表我们可以看到,除了账户表(Account),这些表是针对Blob、Queue、Table三种存储服务来设计的,事实上,Table的存储结构如图4所示:

图4 Table的存储结构
在TableContainer中,记录了每一个Table,在TableRow中,记录的是每一个Table中的实体数据,期中TableRow表中的Data字段以Xml格式记录了Table中实体属性及属性的值。
在本文案例中,我们将实现一个简单图片管理的功能,使用Table存储图片实体数据, 使用Blob存储上传到云端的图片文件,这里只是实现了图片上传功能。
在开始案例之前我们说明一下此案例的开发环境:
VS 2008 SP1/ VS 2010
SQL Express 2005 / SQL Express 2008
Windows Azure Tools for Microsoft Visual Studio 1.1,其下载的地址是:
http://www.microsoft.com/downloads/details.aspx?familyid=5664019E-6860-4C33-9843-4EB40B297AB6&displaylang=en
Windows Azure SDK
http://www.microsoft.com/downloads/details.aspx?FamilyID=21910585-8693-4185-826e-e658535940aa&displaylang=en
在本例中,将使用VS 2010来创建项目,下面我们开始介绍这个案例的详细开发步骤:
第一步:创建项目
在开始|所有程序里打开Microsoft Visual Studio 2010,创建新的Cloud项目DemoStorageTable, 如图5所示:

输入项目名称DemoStorageTable,解决方案名称后,点击“确定 OK”,自动打开添加云应用项目界面,如图6所示:

图6 创建Web Role
选择Asp.Net Web Role,创建名为TableWebRole的项目,选择Worker Role,创建名为TableWorkerRole的项目,点击“确定 OK”。项目创建完成后,就可以在VS里看到刚创建的两个Role以及云计算工程,在这里我们要使用Windows Azure Table,同时还使用了Blob及Queue,会创建图片实体类,这个实体在两个Role中会被使用,同时Queue做为两个Role工程之间的消息传递接口,需要放在公用的一个类库中,因此我们又增加了一个类库:StorageHelper,我们创建的项目结构最终如图7所示:

图7 DemoStorageTable项目工程
在这里由于篇幅,我们只是介绍核心代码的编写。要使用Windows Azure Table,就需要创建相关的实体类,本案例中的实体类是ImageEntity,图片实体类,这个实体类是需要继承TableServiceEntity的,代码如下:
{
public ImageEntity()
{
PartitionKey = DateTime.UtcNow.ToString("yyyyMMdd");
RowKey = string.Format("{0:10}_{1}", DateTime.MaxValue.Ticks -
DateTime.Now.Ticks, Guid.NewGuid());
}
public Uri FileUri { get; set; }
public string FileName { get; set; }
public long Length { get; set; }
public string FolderName { get; set; }
public bool IsReadOnly { get; set; }
public bool IsExists { get; set; }
public string FullFileName { get; set; }
public string ExtendName { get; set; }
public string CreatorName { get; set; }
public DateTime CreateTime { get; set; }
}
创建完图片实体类后,需要接着创建ImageContext,这个类是通过上下文环境对实体进行添加、修改、查询的类,此类需要继承自TableServiceContext,代码如下:
{
private const string ENTITY_NAME = "imageentity";
public ImageContext(string baseAddress, StorageCredentials credentials)
: base(baseAddress, credentials)
{
CloudTableClient.CreateTablesFromModel(typeof(ImageContext), baseAddress, credentials);
}
public IQueryable<ImageEntity> ImageEntity
{
get { return CreateQuery<ImageEntity>(ENTITY_NAME); }
}
/// <summary>
/// Add object to table
/// </summary>
/// <param name="data">ImageEntity object to add</param>
public void Add(ImageEntity data)
{
AddObject(ENTITY_NAME, data);
SaveChanges();
}
/// <summary>
/// Update object
/// </summary>
/// <param name="original">Original ImageEntity object</param>
/// <param name="data">Updated object</param>
public void Update(ImageEntity original, ImageEntity data)
{
original.CreateTime = data.CreateTime;
original.CreatorName = data.CreatorName;
original.ExtendName = data.ExtendName;
original.FileName = data.FileName;
original.FileUri = data.FileUri;
original.FolderName = data.FolderName;
original.FullFileName = data.FullFileName;
original.IsExists = data.IsExists;
original.IsReadOnly = data.IsReadOnly;
original.Length = data.Length;
UpdateObject(original);
SaveChanges();
}
}
有关实体的类编写完成后,就对Table的操作的类(类名:StorageTable)代码进行编写,主要代码这里列出了对添加实体到Table的方法:
/// Add object to table
/// </summary>
/// <param name="data">ImageEntity object to add</param>
public void Add(ImageEntity data)
{
Context.Add(data);
}
这里的Context就是实体操作类的一个实例。
{
get { return new ImageContext(Account.TableEndpoint.AbsoluteUri, Account.Credentials); }
}
类别代码编写完成后,编译没有错误的话,就可以编写Web Role(用户UI层)的代码了,设计好Default页面的功能后,本案例中,放置了一个上传图片的控件,两个文本框,分部用来填写图片名称和创建者,一个上传的按钮,点击按钮,上传图片到Blob中,同时把数据存储到Windows Azure Table中,上传按钮的代码如下:
/// 上传图片
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnSave_Click(object sender, EventArgs e)
{
//获取扩展名
string extension = System.IO.Path.GetExtension(fileUploadControl.FileName);
DirectoryInfo di = new DirectoryInfo(string.Format("{0}\\", conContainerAddress));
FileInfo file = new FileInfo(fileUploadControl.PostedFile.FileName);
//创建Blob并重命名
var blob = _BlobContainer.GetBlobReference(Guid.NewGuid().ToString() + extension);
blob.UploadFromStream(fileUploadControl.FileContent);
//设置元数据到Blob
blob.Metadata["FileName"] = txtFileName.Text;
blob.Metadata["Length"] = fileUploadControl.FileContent.Length.ToString();
blob.Metadata["FolderName"] = di.Name;
blob.Metadata["IsReadOnly"] = file.IsReadOnly.ToString();
blob.Metadata["IsExists"] = file.Exists.ToString();
blob.Metadata["FullFileName"] = file.FullName;
blob.Metadata["ExtendName"] = file.Extension;//extension
blob.Metadata["CreatorName"] = txtCreator.Text;
blob.Metadata["CreateTime"] = DateTime.Now.ToShortTimeString();
blob.SetMetadata();
//设置属性
blob.Properties.ContentType = fileUploadControl.PostedFile.ContentType;
blob.SetProperties();
//存入Table实体数据
StorageTable table = new StorageTable();
table.Add(new ImageEntity
{
CreateTime = DateTime.Now,
ExtendName = file.Extension,
CreatorName = txtCreator.Text,
FileName = file.Name,
FileUri =blob.Uri,
FolderName = di.Name,
FullFileName = file.FullName,
IsExists = file.Exists,
IsReadOnly = file.IsReadOnly,
RowKey = file.Name
}
);
// Add message to queue
StorageQueue queue = new StorageQueue();
queue.Add(new CloudQueueMessage(blob.Uri.ToString() + "$" + file.Name));
//显示列表
DisplayFileList();
txtFileName.Text = string.Empty;
txtCreator.Text = string.Empty;
statusMessage.Text = string.Empty;
}
Web Role的代码开发完成后,转向Worker Role工程,在这里添加Queue进程执行的类,本文的重点在于对Table的开发,因此就不详细讲解了。
第三步:设置服务定义和服务配置文件
在这里就不详细讲解如何设置云计算工程的服务文件了,这里只是给出配置文件的最终列表。
服务定义文件ServiceDefinition.csdef:
<ServiceDefinition name="DemoStorageTable" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
<WebRole name="TableWebRole">
<InputEndpoints>
<InputEndpoint name="HttpIn" protocol="http" port="80" />
</InputEndpoints>
<ConfigurationSettings>
<Setting name="DiagnosticsConnectionString" />
<Setting name="BlobConnectionString" />
</ConfigurationSettings>
</WebRole>
<WorkerRole name="TableWorkerRole">
<ConfigurationSettings>
<Setting name="DiagnosticsConnectionString" />
<Setting name="BlobConnectionString" />
</ConfigurationSettings>
</WorkerRole>
</ServiceDefinition>
服务配置文件ServiceConfiguration.cscfg
<ServiceConfiguration serviceName="DemoStorageTable" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">
<Role name="TableWebRole">
<Instances count="1" />
<ConfigurationSettings>
<Setting name="DiagnosticsConnectionString" value="UseDevelopmentStorage=true" />
<Setting name="BlobConnectionString" value="UseDevelopmentStorage=true" />
</ConfigurationSettings>
</Role>
<Role name="TableWorkerRole">
<Instances count="1" />
<ConfigurationSettings>
<Setting name="DiagnosticsConnectionString" value="UseDevelopmentStorage=true" />
<Setting name="BlobConnectionString" value="UseDevelopmentStorage=true" />
</ConfigurationSettings>
</Role>
</ServiceConfiguration>
第四步:调试运行
对于有些不放心的代码,可以打断点进行调试,如果没有调试的断点,可以选择调试|不调试直接运行,我们最终的运行效果如图8~11所示:

如图8所示,Blob里存储了图片文件, windows-azure-platform.png,不过图8中实例还有点小问题,0字节图片?但不影响我们对开发过程和原理的理解。

如图9所示,Table里存储了我们的文件实体信息,这个实体的字段(属性)是我们在程序里定义的ImageEntity,这也体现了Windows Azure Table良好的可扩展性。

图10 数据中的Table信息
连接上SQL Express后,我们使用T-SQL查询语句:
select * from TableContainer
select * from TableRow
结果如图10所示,点击PartitionKey为06282010的数据,打开窗口如图11所示:

图11 Table XMl实体数据
在TableRow的Data字段里存储了一个XML片段,这个片段描述了我们实体对象的属性和属性值,这样我们就可以通过VS环境中的Server Explorer来查看Table中的数据,并且可以对Table里的数据进行查询。
总结:
通过Windows Azure Table功能特点和架构的介绍,相信大家对于Table已经有了理性的认识,对开发者来说,实战永远是第一位的。相对于Windows Azure Blob和Windows Azure Queue的开发,Windows Azure Table的开发要困难一些,这主要在于进行Table服务开发,首先要开发实现Table的实体类和实体操作类,而且对于有文件操作的案例,免不了要使用Blob存储文件,同时定义了Queue,在Web Role和Worker Role工程之间进行消息传递,可以说这是一个使用了Windows Azure Storage的综合案例。