某ハッカソンで必要な知識だったので試した記録
Azure Function とは
今、話題のサーバーレスとか呼ばれてるやつ。開発者はコードに書くことに集中でき、それ以外は Azure にお任せできる
Visual Studio 2017.3 に標準で Azure Function を開発するための拡張機能が追加されたので触ってみた感じ。
言語は C# を選んだ。 NodeJS を前提に作られてるっぽいけど、今回は Visual Studio 2017 の進化したインテリセンスを使ってみたくて、、、
Azure Table Storage とは
いわゆる、 NoSQL なデータベースで、 Key-Value な感じを採用してる。 今回は、 Azure Function と標準で対応してて相性がよさそうという理由で採用してる
CRUD
データベースに対する基本的な操作の データの作成 © 読み取り ® 更新 (U) 削除 (D) なのをまとめてそう呼んでいます。
実際にどうやるのか
今回操作していくデータ
テーブル名
storeInfo
カラム
- Name
- Locationx
- Locationy
データの作成 ©
作成するテンプレート
Http-POST なテンプレートを使用して作成していきます。
コード例
using System; using System.Net; using System.Net.Http; using System.Threading.Tasks; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.Azure.WebJobs.Host; using Microsoft.WindowsAzure.Storage.Table; namespace FunctionApp1 { public static class PostStoreData { [FunctionName("PostStoreData")] public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post")]HttpRequestMessage req, [Table("storeInfo", Connection = "")]ICollector<Person> outTable, TraceWriter log) { dynamic data = await req.Content.ReadAsAsync<object>(); string name = data?.name; double locationx = data?.locationx; double locationy = data?.locationy; if (name == null || locationx == 0 || locationy == 0) { return req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name in the request body"); } outTable.Add(new Person() { PartitionKey = "Functions", RowKey = Guid.NewGuid().ToString(), Name = name, LocationX = locationx, LocationY = locationy }); return req.CreateResponse(HttpStatusCode.Created); } public class Person : TableEntity { public string Name { get; set; } public double LocationX { get; set; } public double LocationY { get; set; } } } }
データの読み込み ®
使用するテンプレート
Http-GET なテンプレートを使用して作りました。
コード例
using System.Linq; using System.Net; using System.Net.Http; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.Azure.WebJobs.Host; using Microsoft.WindowsAzure.Storage.Table; namespace FunctionApp1 { public static class GetStoreData { [FunctionName("GetStoreData")] public static HttpResponseMessage Run([HttpTrigger(AuthorizationLevel.Function, "get")]HttpRequestMessage req, [Table("storeInfo", Connection = "")]IQueryable<Person> inTable, TraceWriter log) { var query = from person in inTable select person; foreach (Person person in query) { log.Info($"Name:{person.Name}"); } return req.CreateResponse(HttpStatusCode.OK, inTable.ToList()); } public class Person : TableEntity { public string Name { get; set; } public double LocationX { get; set; } public double LocationY { get; set; } } } }
データの更新 (U)
Http-PUT なテンプレートを使用し作りました
コード例
using System; using System.Net; using System.Net.Http; using System.Threading.Tasks; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.Azure.WebJobs.Host; using Microsoft.WindowsAzure.Storage.Table; using Newtonsoft.Json; using System.Linq; namespace FunctionApp1 { public static class UpdateStoreInfo { [FunctionName("UpdateStoreInfo")] public static HttpResponseMessage Run([HttpTrigger(AuthorizationLevel.Function, "put")]Person person, [Table("storeInfo", Connection = "")]CloudTable outTable, TraceWriter log) { if (string.IsNullOrEmpty(person.Name)) { return new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent("A non-empty Name must be specified.") }; }; log.Info($"PersonName={person.Name}"); //outTable // .CreateQuery<Person>() // .Where(personData => personData.Name == "kuxu") // .Select(personData => personData.PartitionKey) // .ToList() // .ForEach(partitionKey => ; TableOperation updateOperation = TableOperation.InsertOrReplace(person); TableResult result = outTable.Execute(updateOperation); return new HttpResponseMessage((HttpStatusCode)result.HttpStatusCode); } public class Person : TableEntity { public string Name { get; set; } public double LocationX { get; set; } public double LocationY { get; set; } } } }
データの削除 (D)
Http-Trigger なテンプレートをもとに作りました。
コード例
using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.Azure.WebJobs.Host; using static FunctionApp1.PostStoreData; using System; using Microsoft.WindowsAzure.Storage.Table; namespace FunctionApp1 { public static class DeleteStoreInfo { [FunctionName("DeleteStoreInfo")] public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "delete", Route = null)]HttpRequestMessage req, [Table("storeInfo", Connection = "")] CloudTable inTable, TraceWriter log) { log.Info("C# HTTP trigger function processed a request."); dynamic data = await req.Content.ReadAsAsync<object>(); string key = data?.key; string rowNumber = data?.rowNumber; if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(rowNumber)) return req.CreateResponse(HttpStatusCode.BadRequest, "Argument Erro"); var excuseResult = inTable.Execute((TableOperation.Delete(new TableEntity() { PartitionKey = key, ETag = "*"}))); HttpStatusCode stateCode; if (excuseResult.HttpStatusCode.ToString().StartsWith("2")) { stateCode = HttpStatusCode.Accepted; } else { stateCode = HttpStatusCode.BadRequest; } return req.CreateResponse(stateCode, $"Code:{excuseResult.HttpStatusCode}"); } } }
値で検索してデータを返す
Http-GET なテンプレートを使用して作成
// 画像は省略
実行例
今回は、 データベースの中にすでに存在している name プロパティを参考に検索するコードを書いていきます。
検索するワードの指定は、 GET リクエストのURLの最後に name={検索したい文字}
で指定しています。
既存のデータ
検索結果
検索ワード: kuxu
検索ワード: chihiro
コード例
using System.Linq; using System.Net; using System.Net.Http; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.Azure.WebJobs.Host; using Microsoft.WindowsAzure.Storage.Table; namespace FunctionApp1 { public static class SearchStoreInfo { [FunctionName("SearchStoreInfo")] public static HttpResponseMessage Run([HttpTrigger(AuthorizationLevel.Function, "get")]HttpRequestMessage req, [Table("storeInfo", Connection = "")]IQueryable<Person> inTable, TraceWriter log) { dynamic data = req.Content.ReadAsAsync<object>(); string name = req.GetQueryNameValuePairs() .FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0) .Value; if (string.IsNullOrWhiteSpace(name)) return req.CreateResponse(HttpStatusCode.BadRequest, "Argument Not found"); var searchResultList = inTable.Where(person => person.Name == name).ToList(); if (searchResultList.Count == 0) return req.CreateErrorResponse(HttpStatusCode.NotFound, "Data Not Found"); return req.CreateResponse(HttpStatusCode.OK, searchResultList); } public class Person : TableEntity { public string Name { get; set; } } } }
感想
ちゃっと作るときは便利ですね~ 目的によっては強い子かも