65.9K
CodeProject 正在变化。 阅读更多。
Home

从 Azure 存储获取数据

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (9投票s)

2010年5月10日

CPOL

7分钟阅读

viewsIcon

45473

downloadIcon

421

如何从 Azure 存储获取聚合数据。

引言

我正在进行一个 POC,并尝试弄清楚如何通过 REST 调用从 Azure 存储获取聚合数据。我的经理让我使用 LINQ 获取聚合数据。我感到震惊。我怎么能应用 LINQ 查询来从 Azure 存储获取数据?REST 通过 Web 服务提供响应。我不想卷入无聊的争论,所以我决定着手研究。经过大量研究,我发现可以通过在表上使用过滤器来优化从 Azure 存储的数据访问。

让我们从头开始。

解决方案

第 1 步: 我创建了一个简单的基于 Windows 窗体的应用程序用于 UI。它看起来如下

我将窗体分为 3 个部分:数据输入、验证到 Azure 和结果。

第 2 步: 提交按钮不仅会通过 REST 验证 URI 和时间,还会解析 REST 响应中的结果。

但在深入研究代码之前,让我们看看我到底在做什么。

我在 Azure 存储中有一个示例表,在我的测试表中,有两个属性:Key 和 Value。假设我们有 key = "CPU" 和 value = 10,key = "Memory" 和 value = 90,依此类推(相似的 key)...当我通过 REST 调用时,它以 XML 形式响应 CPU 和 Memory 的所有数据,但我想在通过 REST 请求时从 Azure 存储获取聚合数据。我不想先将所有数据获取到本地,然后再执行聚合。我想要响应,例如 Key = "CPU" 和 Value = 1450(对应于 CPU 的所有值的聚合总和)。(这就是我必须做的,最终我发现目前这不可能,因为 Azure 不允许我们通过 REST 操作服务器数据。所以,我将这个概念整合为一项伟大的 Windows Azure 想法。)

唯一的解决方案是通过过滤表来减少大型表响应的开销。

第 3 步: 现在,我们如何通过 REST 调用 Azure 存储?有关 REST 的更多信息,请访问此链接

我在这里不描述 REST 架构。所有存储服务都可以通过 REST API 访问。可以在 Windows Azure 中运行的服务内访问存储服务,或者可以直接通过 Internet 从任何能够发送 HTTP/HTTPS 请求并接收 HTTP/HTTPS 响应的应用程序进行访问。您可以通过 REST 访问三个服务(Blob 服务、队列服务和表服务)。对于我来说,我使用的是表服务。

表服务以表的形式提供结构化存储。表服务支持符合 ADO.NET Data Services REST API 的 REST API。开发人员还可以使用 ADO.NET Data Services 的 .NET 客户端库来访问表服务。在存储帐户内的表服务中,开发人员可以创建命名的表。表将数据存储为实体。实体是命名属性及其值的集合,类似于行。表会进行分区以支持跨存储节点的负载均衡。每个表的第一项属性是分区键,它指定实体所属的分区。第二个属性是行键,它标识给定分区内的实体。分区键和行键的组合形成一个主键,该主键在表中唯一标识每个实体。

第 4 步:理论讨论够了,让我们深入代码。

首先声明输入字段(在我的基于窗体的应用程序中)

private string _Account;
private string _Table;
private string _Secret;
private List<string> result;
private List<string> value;
public string key = string.Empty;
  • _Account 用于获取用户 Azure 存储帐户的输入。
  • _Table 用于获取已存在于 Azure 存储帐户中的示例表的输入。
  • _Secret 密钥用于身份验证目的。这是一个重要的字段,因为我们将进一步使用此密钥对 REST 调用中的标头进行签名。

第 5 步: 输入由提交按钮验证。

private void btnSubmit_Click(object sender, EventArgs e)
{
    _Secret = tbSharedKey.Text;
    _Account = tbAccountName.Text;
    _Table = tbTblName.Text;
    
    string xml = string.Empty;
    ListTables(key); // this function will show entitis of table
}

第 6 步:现在我们准备进入 REST。我们将调用 REST API 以从 Azure 存储获取表数据。

表服务 API 符合 ADO.NET Data Services 提供的 REST API。在表服务中,可以执行不同的 REST API 操作(查询表、创建表、合并实体、删除表、插入实体、更新实体和删除实体)。我在这里使用的是查询实体。有关其他操作的详细信息,您可以使用此链接

可以按如下方式构造查询实体请求。将 *myaccount* 替换为您的存储帐户名称,将 *Tables* 替换为您用作输入的示例表:*http://myaccount.table.core.windows.net/Tables*。

此 URI 用于创建 GET 格式的请求以及请求标头。因此,我的 URI 将如下所示

uri = @"https://" + _Account + ".table.core.windows.net/" + Resource;
HttpWebRequest request= (HttpWebRequest)HttpWebRequest.Create(uri);
            
request.Method = "GET";
request.ContentLength = 0;

但是我们需要添加一个标头来调用 REST API。请求标头参数显示在表中

请求标头

描述

Authorization

必需。指定身份验证方案、帐户名称和签名。有关更多信息,请参阅身份验证方案

Date 或 *x-ms-date*

必需。指定请求的协调世界时 (UTC)。有关更多信息,请参阅身份验证方案

x-ms-version

可选。指定此请求使用的操作版本。有关更多信息,请参阅存储服务版本控制

因此,让我们在标头中添加必需的参数

request.Headers.Add("x-ms-date", DateTime.UtcNow.ToString("R", 
        System.Globalization.CultureInfo.InvariantCulture));

请注意:“x-ms-date”指定 UTC 格式的日期(必需)。

现在,我将在代码中对标头进行签名以进行身份验证。

// Add the Authorization header to the request
request.Headers.Add("Authorization", authH);

授权代码是

string authH = "SharedKey " + _Account + ":" + 
  System.Convert.ToBase64String(hasher.ComputeHash(
  System.Text.Encoding.UTF8.GetBytes(signature)));

签名采用一个定义明确的格式,请记住这一点。

// Now sign the request
string signature = "GET\n";

// Content-MD5
signature += "\n";

// Content-Type
signature += "\n";

// Date
signature += request.Headers["x-ms-date"] + "\n";

// Canonicalized Resource
// remove the query string
int q = Resource.IndexOf("?");
if (q > 0) Resource = Resource.Substring(0, q);

// Format is /{0}/{1} where 0 is name
// of the account and 1 is resources URI path
signature += "/" + _Account + "/" + Resource;

现在,使用请求从 Azure 存储获取响应。

HttpWebResponse response = (HttpWebResponse)request.GetResponse()

您现在可以将此响应保存到 StreamReader 对象中。REST 默认将以 XML 格式响应,但您也可以获取 ATOM(类似于 XML)格式的数据。

XML 响应正文如下

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="http://myaccount.tables.core.windows.net/" 
      xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" 
      xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" 
      xmlns="http://www.w3.org/2005/Atom">
  <title type="text">Customers</title>
  <id>http://myaccount.tables.core.windows.net/Customers</id>
  <updated>2008-10-01T15:26:13Z</updated>
  <link rel="self" title="Customers" href="Customers" />
  <entry m:etag="W/"datetime'2008-10-01T15%3A26%3A04.6812774Z'"">
  <id>http://myaccount.tables.core.windows.net/Customers(
                       PartitionKey='Customer03',RowKey='')</id>
    <title type="text"></title>
    <updated>2008-10-01T15:26:13Z</updated>
    <author>
      <name />
    </author>
    <link rel="edit" title="Customers" 
       href="Customers (PartitionKey='Customer03',RowKey='')" />
    <category term="myaccount.Customers" 
       scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
    <content type="application/xml">
      <m:properties>
        <d:PartitionKey>Customer03</d:PartitionKey>
        <d:RowKey></d:RowKey>
        <d:Timestamp m:type="Edm.DateTime">2008-10-01T15:26:04.6812774Z</d:Timestamp>
        <d:Address>123 Lakeview Blvd, Redmond WA 98052</d:Address>
        <d:CustomerSince m:type="Edm.DateTime">2008-10-01T15:25:05.2852025Z</d:CustomerSince>
        <d:Discount m:type="Edm.Double">10</d:Discount>
        <d:Rating m:type="Edm.Int32">3</d:Rating>
      </m:properties>
    </content>
  </entry>
</feed>

这就是我的部分。一旦您在本地端获取了响应数据,您就可以对其进行处理并显示数据。

但是等等,我还没有为您提供问题的解决方案。如果 Azure 存储中有大量数据,那么它将在表数据的响应中产生开销。

到目前为止,REST API 还没有允许通过任何机制获取聚合数据,但是您可以使用特定的 URI 来过滤表数据。过滤将有助于以过滤后的形式获取数据。也就是说,如果我想在本地获取任何特定数据,那么我就不需要从 Azure 存储表请求所有数据。Azure 存储为 URI 提供了查询语法。

例如:要返回单个命名表,请按如下方式指定表:*http://myaccount.table.core.windows.net/Tables('MyTable')*。

对我而言,我使用了过滤器查询语法:*http://myaccount.table.core.windows.net/Customers()?$filter=LastName%20eq%20'Smith'%20and%20FirstName%20eq%20'John'*。

要获取有关更多查询语法的详细信息,请使用此链接

这是我在代码中应用过滤器的方法

if (Key == "")
{
    uri = @"https://" + _Account + ".table.core.windows.net/" + Resource;
}
else
{
    uri = @"https://" + _Account + ".table.core.windows.net/" + 
          Resource +"?$filter=Key%20eq%20"+"'"+Key+"'";
}

很简单!!嗯,还有一件事我想讨论:我如何读取 XML 并将其解析到相关的答案部分。

XDocument xdoc = XDocument.Parse(xml);
doc.LoadXml(xml);
XmlNodeList nodes = doc.GetElementsByTagName("d:Key");// Response XML attrib
List<string> la = new List<string>();
foreach (XmlNode n in nodes)
{
    la.Add(n.InnerText);
}

我将列表绑定到数据源。如果存在重复的键,如我在问题陈述中前面提到的,那么我将读取列表并删除列表中的重复项,然后绑定到组合框。这又是另一个有趣的主题,我的室友建议使用哈希集,但我发现删除列表中的重复项更有趣。

static List<string> removeDuplicates(List<string> la)
{
    Dictionary<string, int> uniqueStore = new Dictionary<string, int>();
    List<string> finalList = new List<string>();

    foreach (string currValue in la)
    {

        if (!uniqueStore.ContainsKey(currValue))
        {

            uniqueStore.Add(currValue, 0);
            finalList.Add(currValue);

        }

    }
    return finalList;
}

最后,我将这个列表绑定并删除列表中的重复项。

结论

我的问题仍然没有解决,但我从过滤器查询语法中获得了一些帮助。这不足以解决我的问题,但我期待着 Azure 存储中的一些新功能。

© . All rights reserved.