Pandora Pocket

IT系と日常系の備忘録。三日坊主。

Azure Functions v3でCosmosDBの基本処理を実行するためのサンプルプロジェクト

せっかくAzureでCosmosDBの無料枠ができたので、使わない手はないなと思って基本的な挙動確認用のプロジェクトを作成してみました。

CosmosDBのデータベース、コンテナ作成から登録更新検索削除の一通りの基本処理をAzure Functionsのv3環境で実装しています。

学習がてらDI実装。Azure CosmosDB Emulator をインストールすればローカル実行可能です(というよりはまだAzure上で確認してない)。

あくまでサンプルなので異常系の対応とかは特に入ってません。

余談

当初せっかくv3環境なのでNewtonsoft.Jsonを用いず、System.Text.Jsonを利用しようと考えていたのですが、うまくいかず断念しました。
プロパティ名は頭を大文字にするため、Newtonsoft.Jsonだと JsonProperty を用いて別名を指定するわけですが、同じ挙動をするはずのSystem.Text.JsonJsonPropertyName だと

The input content is invalid because the required properties - 'id; ' - are missing

というエラーが出てしまい実行に失敗してしまいます。
どうもCosmosDBのv3SDKはまだNewtonSoft.Jsonにがっつり依存しているため、System.Text.Jsonを使いたいならv4までまて、ということのようです。

余談の追記

Version 3.1.2からSerializerOptionsでcamelCase指定ができるようになってたようです。
これを設定しておけばそもそもJsonPropertyの指定も不要ですね。

参考サイト

ParcelでバンドルしたJavaScriptの関数を直接呼び出すときの方法

HTMLのフォームにonSubmitでJavaScriptを呼ぶように実装してたんですが、HTMLファイルに直接JavaScriptを記載していたり、生のJavaScriptファイルを読ませていた時はうまく動くのに、Parcelを通したJavaScriptファイルだと Uncaught ReferenceError となってしまい、実行されない問題が発生。

ググったところそれっぽい情報を発見。

生のJavaScriptファイルがこんな感じの内容だったとして、

Parcelを通すとこんなファイルが生成されます。

モジュール化されることでメソッドがグローバルに定義されなくなるので、onSubmit="hogehogeMethod()"と呼んでも失敗すると。

であればグローバル下に引きずり出してやればいいと。

というわけで、

こんな感じで、

window.メソッド名 = メソッド名;

というようにグローバルに定義してやればonClickやonSubmitといったイベントハンドラから呼び出すことが可能。

指定時間にディスプレイの電源をオンオフするだけのミニツールを作るなどした

普段テーブルの端にSurface Proを置き、Twitterクライアントを常時起動させつつ、音楽を再生したりしています。
朝起きた時に夜中のツイートもささっと流し読みしたいので24時間電源を入れっぱなしなのですが、使っていない時間ずっとディスプレイがつきっぱなしなのは電気代がもったいない。かといって電源設定でディスプレイの電源を切る時間を設定すると、普段見たいときにいちいち画面タップするなりしないといけないので面倒。

というわけで、指定した時間にディスプレイの電源をオンオフするだけの機能を持ったタスクトレイ常駐アプリを作りました。

続きを読む

Entity Framework 3.0 Coreだと LastOrDefaultが利用できない

よく考えればそりゃそうか感はあるんですが。

Entity Framework 3.0 Coreでは破壊的変更としてLINQ クエリがクライアントで評価されなくなった という修正があります。

.NETの関数を内部的に使っているなどして、SQLに変換できない系統のものが動かなくなったんですが、それに関係なさそうな LastOrDefault も利用できなくなっていました。

例えばこんな感じのLINQ

await Context.Set()
.Where(r => r.COLUMN1 == "hoge")
.Select(s => s.COLUMN2)
.LastOrDefaultAsync();

GitHubのIssueを見てると該当するものが。

対応としては OrderByDescending()を使って明示的に降順に並び替えてからFirstOrDefaultAsync()を呼び出す感じ。
LastOrDefaultAsync()だと結果が確定的でない可能性があるので、厳密に判定できるようにこういう対応をしたようで。

つい最近のEFCoreへのP-rで、もう少しわかりやすいエラーメッセージになるようです。

ListからSQL検索条件を生成するLINQ

.NET Core 3.1対応をしているのですが、それに伴ってEntity Framework Coreもv3にあげたところ、LINQのクライアントサイド評価が行われなくなったことでいくつかのクエリが使えなくなったため、いろいろ調べながら対応しています。

対応が必要だったうちの一件が下記のようなもの。

class TBL {
string Data1;
string Data2;
}

このようなクラスを保持するリスト(List)に対して、

.Where(x => list.Any(l => l.Data1 == x.Data1 && l.Data2 = x.Data2))

のように、含まれている値と一致するレコードだけを取得するというものです。
SQLにしてみれば

WHERE (TBL.Data1 == [listの値1-1] && TBL.Data2 == [listの値1-2])
OR (TBL.Data1 == [listの値2-1] && TBL.Data2 == [listの値2-2])
・・・

みたいな感じ。
LINQ to SQLだとこのクエリ、SQLに変換してサーバーサイドで実行してくれないんですね。
式木をうまいことこねこねして動的に.Where内の値を生成するようにしようと思ったのですが、今の力量だとちょっと手間取りそうで、それに時間をかけてられないこともあり、とりあえずLINQを使わずFromSqlRawで直接SQLを実行することにしました。
SQLで実行するにしてもWHERE句に指定する条件はリストから動的に生成する必要があります。

そんな時に便利なのが下記のLINQ

var param = list
.Aggregate(
string.Empty,
(current, tbl) => current + $" OR ([Data1] = '{tbl.Data1}' AND [Data2] = {tbl.Data2}) ");

これならlistに格納されている値分のOR条件式が一つの文字列に吐き出されるので、あとは

var sql = $@"SELECT * FROM TBL
WHERE (1=0)
{param}";

こんな感じでSQLにぶち込んで、EFなりDapperなりに渡してやればOK。