Windows Azure实战:如何创建云数据存储
2010-05-21 00:00:00 来源:WEB开发网Windows Azure SDK开发环境包括一个开箱即用的开发存储(Development Storage),实际上它是一个在本地模拟云存储服务的工具,Azure SDK提供了开发存储服务,在将应用程序部署到云之前,允许开发人员在本机创建、调试和测试云数据服务。默认情况下,开发存储是以SQL Server Express版为基础模拟云存储环境的,开发存储使用Windows身份认证连接到SQL Server Express。本文将向大家展示如何在本地云开发环境中创建一个数据表。
第1步:创建新的Worker Cloud Service项目
使用Visual Studio中的Worker Cloud Service模板创建一个新项目,命名为CreateDataStorage。
图 1 创建新的Worker Cloud Service项目
第2步:添加StorageClient.dll引用
Visual Studio默认会为这个解决方案生成两个项目,需要给项目添加一个到StorageClient.dll的引用,这个程序集在安装Windows Azure SDK的目录下,如我这里是C:\Program Files\Windows Azure SDK\v1.0\Samples\StorageClient\Lib\bin\Debug,如果没有这个文件,那么就需要载入SDK自带的示例,重新编译一次即可生成这个文件。
public class Address : TableStorageEntity
{
private State? _state;
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public int? State { get { return (int)_state; } set { _state = (State)value; } }
public string Zip { get; set; }
public string County { get; set; }
public string Country { get; set; }
public Address():this(Guid.NewGuid())
{
}
public Address(Guid internalID)
: base(ConfigurationManager.AppSettings["PartitionKey"], internalID.ToString())
{ }
public Address(string address1,
string address2,
string city,
State state,
string zip,
string county,
string country,
Guid internalID)
:this(internalID)
{
Address1 = address1;
Address2 = address2;
City = city;
State = (int)state;
Zip = zip;
County = county;
Country = country;
}
}
第3步:添加新的C#库项目CloudData.Models
接下来给解决方案添加一个C#库项目CloudData.Models,在这里,我定义了一个很简单的数据实体类Address,它继承了SDK基础类 Microsoft.Samples.ServiceHosting.StorageClient.TableStorageEntity,下面的代码显示了这个类的定义。
第4步:创建CloudDataService文件夹
在同一个项目中,创建一个CloudDataService文件夹,在它上面增加三个接口定义 – IdataTableService,IHasDependencyTableService和INoDependencyTableService。 IDataTableService接口暴露三个基本的数据表访问功能 – Insert,Update和Delete,所有这些功能都接受 Microsoft.Samples.ServiceHosting.StorageClient命名空间中定义的TableStorageEntity 类参数;其它两个接口都衍生自IDataTableService接口,IHasDependencyTableService接口暴露 UpdateDependencyTable()方法,它也接受TableStorageEntity类参数;INoDependencyTableService接口不暴露任何方法,但它提供了一个类型定义,用于标记一个类为无逻辑依赖数据对象。
第5步:在CloudDataService文件夹下添加两个类
接下来在CloudDataServices文件夹中增加两个类 – DataTableService和AddressTableService,将第一个类标记为抽象类,AddressTableService类是DataTableService类的衍生类。
所有类和接口的代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CreateDataStorage.Models.CloudDataServices
{
using Microsoft.Samples.ServiceHosting.StorageClient;
public interface IDataTableService
{
bool Insert(TableStorageEntity entity);
bool Update(TableStorageEntity entity);
bool Delete(TableStorageEntity entity);
}
public interface IHasDependencyTableService : IDataTableService
{
bool UpdateDependencyTable(TableStorageEntity entity);
}
public interface INoDependencyTableService : IDataTableService
{
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CreateDataStorage.Models.CloudDataServices
{
using Microsoft.Samples.ServiceHosting.StorageClient;
using CreateDataStorage.Models.CloudDataContext;
abstract public class DataTableService
{
protected UserDataContext _userDataContext = null;
protected string _Table { get; set; }
public DataTableService()
{
_userDataContext = new UserDataContext();
}
public UserDataContext DataContext() { return _userDataContext; }
virtual public bool Insert(TableStorageEntity entity)
{
bool success = false;
try
{
if (this is IHasDependencyTableService)
{
(this as IHasDependencyTableService).UpdateDependencyTable(entity);
}
_userDataContext.AddObject(_Table, entity);
_userDataContext.SaveChanges();
success = true;
}
catch { }
return success;
}
public bool Update(TableStorageEntity entity)
{
bool success = false;
try
{
if (Delete(entity))
{
success = Insert(entity);
}
}
catch { }
return success;
}
virtual public bool Delete(TableStorageEntity entity)
{
bool success = false;
try
{
if (this is IHasDependencyTableService)
{
(this as IHasDependencyTableService).UpdateDependencyTable(entity);
}
_userDataContext.DeleteObject(entity);
_userDataContext.SaveChanges();
success = true;
}
catch { }
return success;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
namespace CreateDataStorage.Models.CloudDataServices
{
using Microsoft.Samples.ServiceHosting.StorageClient;
using CreateDataStorage.Models.CloudData;
public class AddressTableService : DataTableService, INoDependencyTableService
{
public AddressTableService()
{
_Table = ConfigurationManager.AppSettings["AddressTable"];
}
}
}
第6步:添加UserDataContext类
在同一个项目上,添加UserDataContext类,这个类衍生自Azure SDK基础类TableStorageDataServiceContext,它封装了一个云存储查询函数,UserDataContext的定义代码如下:
public class UserDataContext : TableStorageDataServiceContext
{
public DataServiceQuery<Address> AddressTable
{
get
{
return
CreateQuery<Address>(ConfigurationManager.AppSettings["AddressTable"]);
}
}
}
第7步:指定数据库名
在CreateDataStorage项目节点上点击右键,选择“属性”,在属性对话框中,选择左侧的“开发”,输入数据库名AzureForDotNetDeveloper,如下图所示。
图 2 输入数据库名
第8步:添加CloudData.Models.dll引用
给CloudStorageService_WorkerRole项目添加CloudData.Models.dll引用。
第9步:创建测试存储表
在CreateDataStorage项目上点击右键,选择“创建测试存储表(Create Test Storage Table)”,如下图所示,编译器将会分析代码,为数据库生成一个表。
图 3 创建测试存储表
生成的表如下图所示。
图 4 生成的AddressTable表
第一次在本地创建Development Storage(开发存储)时,会弹出一个提示消息框,如下图所示。
图 5 第一次创建开发存储时的提示框
点击“是”,弹出如下图所示的对话框。
图 6 开发存储初始化确认对话框
启动成功后,可以查看其运行状态,如下图所示。
图 7 查看开发存储的运行状态
这就是使用Windows Azure SDK开发存储表服务创建云数据存储表的基本步骤,但实际上,在云环境中的存储利用率和在本地环境中的存储利用率有所不同,因此我们应该遵循一些规则和最佳实践。
为数据列使用可移植型数据类型
数据列的数据类型应该可移植到SQL数据类型,或可以被转换为系统定义的基本数据类型。
使用Windows Azure开发工具生成数据表
使用Azure SDK提供的开发工具调用开发存储服务生成数据库和数据表,开发工具分析所有项目的数据对象生成数据结构,数据表的数量等于衍生自TableStorageEntity类的数据实体类的数量,列的数量等于该数据实体类公共访问属性的数量。
非可移植数据类型解决方案
如果数据类型是自定义的,SDK开发工具将不能调用DevelopmentStorage.exe和DevtableGen.exe,下面的例子展示了在数据表中使用自定义数据列类型的解决方案,我们定义了两个数据对象类 - State和Address,State是枚举类型,它有59个成员,代表美国邮政服务使用的州,每个Address类有一个使用这个自定义类型的属性成员。这两个类的定义如下:
public enum State
{
AL,AK,AS,AZ,AR,CA,CO,CT,DE,DC,FM,FL,GA,GU,HI,
ID,IL,IN,IA,KS,KY,LA,ME,MH,MD,MA,MI,MN,MS,MO,
MT,NE,NV,NH,NJ,NM,NY,NC,ND,MP,OH,OK,OR,PW,PA,
PR,RI,SC,SD,TN,TX,UT,VT,VI,VA,WA,WV,WI,WY
}
public class Address : TableStorageEntity
{
...
public State State { get; set; }
...
}
当我们再次调用创建测试存储表时,将会要求我们移除数据库中的现有存储,如下图所示。
图 8 删除现有存储表的确认对话框
点击“是”,弹出一个执行失败的对话框,如下图所示。
图 9 创建开发存储表失败的消息提示框
Visual Studio输出窗口显示的错误消息是:
DevTableGen(0,0): error DG10: No tables were generated. Either no candidate classes were
found or they did not meet the requirements for the table storage.
光从这个错误输出信息看不出失败的原因,我们可以通过以下步骤重新生成数据表:
1、让State类型继承int类型,因为int是系统定义的类型,是可移植到SQL数据库的;
2、在Address类中,使用State类型定义一个成员变量_state;
3、应用.NET可为空设计模式给这个成员变量;
4、执行类型转换。
下面是修改后的代码:
public enum State : int
{
AL,AK,AS,AZ,AR,CA,CO,CT,DE,DC,FM,FL,GA,GU,HI,
ID,IL,IN,IA,KS,KY,LA,ME,MH,MD,MA,MI,MN,MS,MO,
MT,NE,NV,NH,NJ,NM,NY,NC,ND,MP,OH,OK,OR,PW,PA,
PR,RI,SC,SD,TN,TX,UT,VT,VI,VA,WA,WV,WI,WY
}
public class Address : TableStorageEntity
{
private State _state;
public int? State
{
get { return (int)_state; }
set { _state = (State)value; }
}
}
完成修改后,使用Visual Studio重新生成表,错误将会消失,在本地SQL数据库中重新创建表成功。
数据上下文类继承
除了数据实体类外,对于每一个数据存储表,必须定义一个继承自TableStorageDataServiceContext的类,如:
public class UserDataContext : TableStorageDataServiceContext
{
...
public DataServiceQuery<Address> AddressTable
{
get
{
CreateQuery<Address>(ConfigurationManager.AppSettings["AddressTable"]);
}
}
...
}
使用PartitionKey和RowKey组织分散的数据
为了支持负载均衡,云中的表和实体都是跨存储节点分区的,也就是说,物理上可能是位于不同服务器上的,每个分区容纳连续范围的实体,它们的分区键值相同,分区就是通过分区键组织的。我们在表上指定了PartitionKey属性为分区键,分区键必须是唯一的,分区键是由实体主键和RowKey组成的。
对于每一个数据表实体,数据可以根据PartitionKey和RowKey组织,PartitionKey和RowKey的值可以允许是空字符串,但不能是null,下表显示了PartitionKey和RowKey可能的组合。
表 1 使用PartitionKey和RowKey组织表结构
PartitionKey | RowKey | 使用条款 |
空字符串 | 空字符串 | 一个分区或一行 |
有值 | 空字符串 | 多个分区或一行 |
空字符串 | 有值 | 一个分区或每个分区有多行 |
有值 | 有值 | 多个分区或每个分区有多行 |
更多精彩
赞助商链接