云计算 频道

使用Azure Table的一个简单方法

  【IT168 微软云计算博客征文活动专稿Azure Table介绍

  Microsoft Azure提供了几个不同的存储选项,包括存储文件的Blob,传递消息的Queue,高度可扩展的,支持数据分级的Table,此外,Microsoft Azure也提供了关系数据存储,它是通过在SQL Azure上使用传统的机制实现的。本文介绍一种使用Azure Table存储数据的简单方法。

  我们知道Azure Table是在云中为我们存储数据,为运行在Azure或其它任何地方的应用程序提供数据服务的,Azure Table是构建在REST协议上的,任何平台都可以很容易连接和使用Table中的数据,大部分.NET开发人员都会使用Microsoft Azure SDK提供的客户端存储库来访问Azure Table。

  在开始前,我们先简要地看一下Table是如何工作的,当你创建一个Azure订阅账号后,就可以用这个账号登录到Azure门户,然后就可以创建一个存储账号,与存储账号关联的是一个存储容器,你可以用这个存储账号使用多种存储类型,包括Table,Blob和Queue。

  在云中创建Table和销毁Table都很简单,与传统数据库相比,这是它的主要优势,在创建Table时感觉不到任何延迟和系统开销,应用程序启动时可以查看它需要的Table是否已经创建好,如果还没有,可以就在这个时候立即创建,甚至可以在这个时候将默认数据填充到Table中。

  每个Table是一个实体集,和你在程序中使用的实体对象基本上一样,每个实体有它自己的属性和值,这意味着每个实体有它自己的模型,这是一个好消息,在设计和运行期间可以提供最大的灵活性,这也是和传统关系数据库之间的最大不同。

  创建Azure Table

  下面我们开始构建一个Azure Table,然后写一点代码来使用它,Table可以先在我们的本地Development Dtorage Fabric中运行,然后移动到云中投入生产。我们要写的代码是一个简单的命令行程序,重点放在与Table交互的核心代码上,你会发现我们只用了一行代码来创建Table,我们不需要任何代码来创建列、存储过程、函数或索引。

  我们将构建一个小型应用程序跟踪停车场服务人员发出的停车票,首先我们要创建一个实体类,这个类包含系统的旧数据,即需要跟踪的每张票据,下面是这个类的定义代码:

  

public class ParkingTicket

  {

  
public string LotID { get; set; }

  
public string TicketID { get; set; }

  
public DateTime DateIssued { get; set; }

  
public int AttendentID { get; set; }

  
public string CarTagNumber { get; set; }

  
public string FineAmount { get; set; }

  
public string CauseForTicket { get; set; }

  }

  这是一个简单的实体类,它的唯一工作是容纳我们需要用到的停车票数据,它的几个属性都是按C#编程的自动化属性定义的,编译器会自动创建和管理隐藏的私有字段。

  这个实体定义了Azure Table要存储的数据,这个实体的每个属性都有一个Table属性与之对应,此外,Azure Table针对每个实体还有三个特殊的属性,它们有助于Azure扩展和管理数据,这三个特殊属性是:

  1、PartitionKey – 扩展实体所属组

  2、RowKey – 实体的唯一ID(结合PartitionKey使用时)

  3、DateTimeStamp – 实体最近一次更新的时间戳

  PartitionKey和RowKey结合使用时,可为实体创建一种混合主键,PartitionKey帮助Azure扩展数据,Table中有相同PartitionKey的实体作为一个组一起管理,这种分区方法可以动态扩展系统。

  假设你的Table有上百万行数据,有上千个分区,随着系统负载的增加,有些分区承载的负载会越来越大,在我们的例子中,某个停车场上可能繁忙,那么它对应的分区也会很繁忙,这个时候可以将这样的热点分区移动到独立的存储服务器上,以提高查询响应速度,随着分区负载的变化,它们可以自动移动存储服务器,保证永远都有用不完的硬件资源。

  PartitionKey可以是任意字符串,分区的诀窍是为你的键选择一个策略,要满足扩展和常见查询的需求,在我们的例子中,使用停车场ID作为PartitionKey属性,因为我们想按照停车场对票据进行分组。

  RowKey也可以是任意字符串,PartitionKey和RowKey结合使用时,在整个Azure Table中必须是唯一的。我们不想将这三个特殊属性硬编码进这个实体类,因为它们和我们的实体类毫无关系,否则会和我们的数据混合在一起,在类与Azure Table耦合时会带来麻烦,为了避开这个麻烦,我们的实体类需要继承TableServiceEntity,此外,我们的代码还需要引用两个Azure程序集,Microsoft.WindowsAzure.ServiceRuntime和Microsoft.WindowsAzure.StorageClient。

  我们需要更新一下LotID和TicketID,以便让它们分别作为PartitionKey和RowKey进行跟踪,因为要做一些比较特殊的操作,因此这两个属性不能使用自动化属性。

  

  public class ParkingTicket : TableServiceEntity

     {

        
private string _lotID;

        
public string LotID

         {

            
get { return _lotID; }

            
set

             {

                 _lotID
= value;

                 PartitionKey
= value.ToLower();

             }

         }

        
private string _ticketID;

        
public string TicketID

         {

            
get { return _ticketID; }

            
set

             {

                 _ticketID
= value;

                 RowKey
= value.ToLower();

             }

         }

        
public DateTime DateIssued { get; set; }

        
public int AttendentID { get; set; }

        
public string CarTagNumber { get; set; }

        
public string FineAmount { get; set; }

        
public string CauseForTicket { get; set; }

     }

  使用Azure Table

  做了这些改进后,我们的实体类就可以存储到Azure Table了,注意我们不会硬编码Table的名字,或如何连接它,实体类仅仅表现数据,没有行为或配置。

  我们需要创建一个“context(上下文)”类,由它来掌控数据存放在哪里,以及如何使用这些数据,Azure SDK提供的客户端存储库包含了WCF数据服务上下文模型,你只需要继承它,这个上下文类知道如何与Azure Table中的数据交互。

  我还需要增加一个ParkingTicketContext类,它需要继承TableServiceContext,大部分工作由这些基础类实现了,我们只需要提供一些基本的信息以启动它。

  

public class ParkingTicketContext : TableServiceContext

  {

  
private string tableName = ConfigurationManager.AppSettings["ticketTableName"];

  
public ParkingTicketContext(string baseAddress,

  StorageCredentials credentials) :

  base(baseAddress, credentials) { }

  
public IQueryable ParkingTicket

  {

  
get

  {

  return this.CreateQuery(tableName);

  }

  }

  }

  首先我们需要从app.config文件中获取存储票据的Table名称,我们也需要为类提供一个构造函数,由它接受连接云的凭据,最后需要提供一个通用查询接口,使用WCF数据服务时,你可以使用LINQ轻松查询我们的数据。

  现在我们有实体类容纳数据,有上下文类知道如何与数据源(即Azure Table)通信,现在我们需要一个服务类控制与停车票实体交互时的行为。

  这个服务类是一个普通的类,不需要继承任何东西,它将通过配置抓取我们的Azure凭据,以及存储票据的Table名,我们也将声明一个上下文类的实例,这个服务类为我们提供了大量的服务,当我们想与停车票交互时,特别是当我们想读、写和查询票据表时,将专门通过这个服务工作,请不要将这个类的服务与Web Service中的服务概念混淆了。

  首先,我们增加一个方法返回Table中的所有票据,只需要执行一个简单的LINQ查询即可。

  

public static IEnumerable GetAllTickets()

  {

  try

  {

  var tickets
= (from item in _ParkingTicketContext.ParkingTicket

  
select item);

  return tickets.ToList();

  }

  catch (Exception)

  {

  return
null;

  }

  }

  如果想返回单张票据,只需要修改LINQ表达式按ticketID查询即可,在这个例子中,我们将使用First()返回单一对象。

  

public static ParkingTicket GetParkingTicketByID(string TicketID)

  {

  try

  {

  var theParkingTicket
= (from item in _ParkingTicketContext.ParkingTicket

  where item.RowKey
== TicketID.ToLower()

  
select item).First();

  return theParkingTicket;

  }

  catch (Exception)

  {

  return
null;

  }

  }

  使用上下文类和WCF数据服务将对象保存到Table中相当简单,我们只需要创建一个对象,如:

  

ParkingTicket aParkingTicket = new ParkingTicket

  {

  CarTagNumber
= "SAMPLE",

  AttendentID
= 31415,

  CauseForTicket
= "Slippery Parking",

  DateIssued
= DateTime.Now,

  FineAmount
= "134.50",

  LotID
= "15",

  TicketID
= System.Guid.NewGuid().ToString()

  };

 

  要保存它,只需要将它增加到上下文中,然后调用save方法,上下文类会先将对象保留在内存中,直到我们调用SaveChanges()方法,在调用SaveChanges()方法前,我们也可以通过上下文类增加和移除对象。

  

_ParkingTicketContext.AddObject(ticketsTableName, aParkingTicket);

  _ParkingTicketContext.AddObject(ticketsTableName, aSecondParkingTicket);

  _ParkingTicketContext.SaveChanges();

  使用Azure Table的一些限制和优点

  现在你知道使用Azure Table存储和检索数据是多么简单了吧,但它也存在一些限制,包括:

  · Azure Table最大只能存储100GB数据

  · Table没有任何关系

  · 没有外键

  · 没有连接,要连接只能在本地代码中实现

  · 不能跨表查询,要跨表查询只能使用LINQ

  Azure Table比运行本地数据服务器或SQL Azure要便宜,$0.15/GB,带宽不限,每10,000次读或写再加$0.01,与存储大型视频和图像的Azure Blob相比,Azure Table的费用算是很便宜了。

  本文所写的代码既可以运行在云中,也可以运行在本地数据中心,甚至可以直接运行在桌面计算机上,都可以访问到Azure Table中的数据,这都得感谢OData和REST协议,是它们让数据部署和程序可以完全分离开,OData是微软重点推广的一个协议,WCF数据服务就通过它访问数据的。

0
相关文章