(Translated by https://www.hiragana.jp/)
neue cc - 2015-10

LINQPad Driver + LINQ to BigQueryによるBigQueryデスクトップGUIクライアント

Happy signed!なにかというと、ながらく署名しょめいいていなかったGoogle APIの.NET SDKに署名しょめいいたのです!署名しょめいくとなにができるかというと、LINQPadのDriver(プラグイン)がつくれます。LINQPadのDriverは署名しょめいなしだと起動きどうできないので……。正直しょうじきわたしももはや署名しょめいとか全然ぜんぜん重視じゅうししてないし100おくねんまえ化石かせき概念がいねんまけ異物いぶつだろ、ぐらいにおもってなくもないのですが、さすがに、LINQPad Driverをつくれない、という事態じたいには随分ずいぶんなげいたものでした。が、やっとつくることが出来でき感無量かんむりょう。そして、実際じっさいうごかしてみると相当そうとう便利べんりですね。これがやりたかったんですよ、これがー。

LINQ to BigQueryのLINQPad Driverが可能かのうにする範囲はんいは、

  • サイドバーでのスキーマのツリー表示ひょうじ
  • thisをんでいるConnectionで認証にんしょうみのBigQueryContextに変更へんこう
  • 関連かんれんするアセンブリと名前なまえ空間くうかん自動じどう
  • スキーマに対応たいおうするクラスを動的どうてき生成せいせい/
  • ちょっとしたユーティリティDumpの追加ついか(DumpRun/DumpRunToArray/DumpChart/DumpGroupChart)
  • もちろんクエリのローカルでの保存ほぞん/みが可能かのう

です。元々もともとのLINQ to BigQueryが提供ていきょうしている機能きのうとしては

  • TableDateRangeにたいするサポート
  • DateTimeの自動じどう変換へんかん(一部いちぶのBigQueryの機能きのうはUnix Timestampで必要ひつようがあり、実質じっしつしゅくのは不可能ふかのうなものもありましたが、自動じどう変換へんかんによりすくわれる)
  • 結果けっかセットをローカル時間じかん自動じどう変換へんかん基本きほんてきにUTCでかえってくるので、ローカル時間じかんかんがえるさいに+9時間じかんしなきゃいけなかったりしますが、C#がわでデシリアライズするさいにローカルタイムに自動じどう変換へんかんする)
  • すべてが型付かたつきで入力にゅうりょく補完ほかん全面ぜんめんてき
  • すべてのBigQuery関数かんすう入力にゅうりょく補完ほかんにドキュメント

があって(このあたりくわしいはなし以前いぜんいたLINQ to BigQuery - C#による型付かたつきDSLとLINQPadによるDumpと可視かしてください)、相乗そうじょう効果こうかでかなりつよまったのではないでしょうか。

公式こうしきウェブコンソールでたたくのとどっちがいいかといったら、まぁわたし自身じしん結構けっこう、ウェブからたたくのはおおかったりしますので、どっちでもいいといえばいいんですが、それもプラグインをつくまえは……かしら。今後こんごわたし自身じしんもLINQPad利用りようえるかなー。あきらかにウェブからたたくのじゃ提供ていきょうできない機能きのうというか、もとのBigQuery SQLじゃなか々できない機能きのうおお提供ていきょうしているわけで、LINQPad + LINQ to BigQUeryにはかなりのアドバンテージがあります。

Excel統合とうごう

問答もんどう無用むよう愚直ぐちょくなExcel統合とうごうがあります。

legendary_dump_to_excel

そう、DumpToExcel()で実行じっこうすると結果けっかセットがダイレクトにExcelでひらく……。しかし実際じっさいこういうのでいいんだよこういうのでかんあります。Excelでクエリけい統合とうごう面倒めんどうくさい(実際じっさいアレはダルいのでない)。いちいちCSVにとしてひらくのは面倒めんどうくさすぎる。LINQPadでクエリく、結果けっかがExcelでれる。あとはピボットテーブルなりできに分析ぶんせきできる。そう、そういうことなんですよ、これなんですよ #とは

かた

ExplorerのAdd Connection→View More Drivers からLINQ to BigQueryをさがして、clickでインストールできます。簡単かんたん

image

かなりうえほうのいい位置いちれてもらいました!

using static

BigQueryの関数かんすうはLINQ to BigQueryではBqFunc以下いかめるかたちをとっていますが、C# 6.0から(Javaのように)静的せいてきメソッドのインポートが可能かのうになりました。また、LINQPad 5でもスクリプトのバックエンドがRoslynになり、C# 6.0にフル対応たいおうしています。LINQ to BigQueryのDriverでは、LINQPad 5以上いじょうませた場合ばあいのみ、using static BigQuery.Linq.BqFunc が自動じどうインポートされます。

これにより、クエリをいたさいがより自然しぜんに、というかウザったいBqFuncが完全かんぜんりました!関数かんすうめいおぼえていない、ウロおぼえのときはBqFunc.をしてさがせるし

image

れきった関数かんすうなら、直接ちょくせつくことができる。完璧かんぺき

How to make LINQPad Driver

むずかしいようでむずかしくないようでむずかしいです。しっかりしたドキュメントとサンプル付属ふぞくしているので、スタートはそれなりにスムーズにけるかとおもいます。ひとつ、大事だいじなのはプラグイン開発かいはつだからってデバッグ環境かんきょう妥協だきょうしないでください。ふつーの開発かいはつおなじように、F5でVisual Studioががってすぐにブレークポイントってステップ実行じっこうできる環境かんきょうきずきましょう。こまかいハマりどころがおおいので、それ出来できないとくじけます。ぎゃく出来できてれば、あとは気合きあい、かな……?こまかいやりかたはここにくには余白よはくが(以下いかりゃく

わったハマりどころとしては、たとえば別々べつべつばれるメソッドあいだ変数へんすうわたしたいなー、とおもってprivate fieldにくと、そもそも都度つど頻繁ひんぱんにコンストラクタがばれて生成せいせいされなおすので、共有きょうゆうできない。なるほど、じゃあせめてstatic変数へんすうだったらどうだろうか?というと、LINQPadの内部ないぶ実行じっこう環境かんきょう都合つごうじょう、AppDomainがガンガンられてんでるので、static fieldすらえる!マジか!なるほどねーきびしいねー、などなど。

ちなみに動的どうてきなアセンブリ生成せいせいではCodeDomのCSharpCodeProviderを利用りようしています。つい先月せんげつMetaprogramming Universe in C# - 実例じつれいるILからRoslynまでの活用かつようれいでCodeDomはオワコン、使つかわないとかってたくせに!したかわかぬうちに自分じぶん使つかうことになるとはおもわなかった!

まとめ

社内しゃないでのBigQuery活用かつようほうとして、定形ていけいクエリのダッシュボードはDomoにより可視かし、アドホックなクエリはLINQPad + LINQ to BigQueryによりクエリを色々いろいろいたり、そのままExcelにおくんで(LINQPadはデスクトップアプリなので、DumpToExcel()メソッドとかをつくることによりシームレスに結果けっかセットをExcelにんだりできるのもつよい)PowerPivotでこねくりまわしたり、などをしてます。とはいえ、いままでは事前じぜんにスキーマに対応たいおうするクラスを生成せいせいして保存ほぞんしておかなければならないという面倒めんどうくささがあったので、イマイチ活用かつようしきれてなかったのも事実じじつ実際じっさいわたし自身じしんですらBigQueryの公式こうしきウェブコンソールでクエリたたいたりがおおかったですし。それが、今回こんかいのLINQPad Driverにより圧倒的あっとうてき利便りべんせいがった(というかまえのがもはや原始時代げんしじだいえる)ので、使つかえる度合どあいが桁違けたちがいにがったんじゃないかなー、とおもいます。

デスクトップGUIクライアントの便利べんりさは、たとえばMySQLだったらウェブでphpMyAdminよりもHeidiSQLやMySQL Workbenchのほうが100おくばい便利べんりなわけでして、いところ沢山たくさんあるんですよね。BigQuery関連かんれんだとCloud DataLabなんかもちょうどましたが、ウェブとデスクトップ、それぞれさがあるので、ここはうまく使つかけていきたいところです。

最近さいきんのBigQueryのアップデートへの追随ついずいだと、しんメソッドは全部ぜんぶ実装じっそう完了かんりょうしてます。また、GroupByへのRollupなど文法ぶんぽう追加ついかもOK。ただ、おおきな目玉めだまであるUDF(User Defined Function)への対応たいおうがまだです。べつにそんなむずかしくもないんですが、APIの馴染なじませかたどうしようかな、とかおもってるあいだにLINQPad Driverの作成さくせい時間じかんわれたので、対応たいおうれるのはちかいうちの次回じかいということで。

同期どうきふう)コードと対比たいひさせたUnity+UniRxで非同期ひどうきあつか場合ばあいのパターンしゅう

UniRxのGitHubのStarすうが500きました!

image

いまのところGitHubじょうでのUnity + C#でスターじゅん検索けんさくだと、世界せかい5です。おおー。さらうえねらいたいところですね。最近さいきんはちょっと更新こうしんとどこおっていますが、ネタはあるのでより完成かんせいたかめたい。(とどこおった理由りゆうは、PhotonWireとか色々いろいろのところにしていたため……)

さて、本題ほんだい。イベント結合けつごう使つかさいはあてはまりませんが、Rx(UniRx)を非同期ひどうきながさ1のIOservableシーケンス)としてあつか場合ばあい、それなりにくせがあります。とはいえ、基本きほんてきには同期どうきあるいはyield return)でいていたさいと、1:1で対比たいひできるパターンしたかたちおおむ対応たいおうできるので、そのためのチートシートをかんがえてみました。コードれいはC# 5.0のasync/awaitでしますが、同期どうきコード or IEnumeratorとおなじようにおもってもらえればいいです。たとえば

public void Sync()
{
    /* before action */
    Method();
    /* after action */
}

public IEnumerator IEnumerator()
{
    /* before action */
    yield return StartCoroutine(Method());
    /* after action */
}

public async Task Task()
{
    /* before action */
    await MethodAsync();
    /* after action */
}

みたいなかんじです、awaitに馴染なじみのないひとも、なんとなくイメージしながらながめてみてもらえるとうれしいです。

非同期ひどうき汚染おせん

コードれいまえ非同期ひどうき汚染おせんあるいは非同期ひどうき伝搬でんぱんについて。まぁ、あんまし汚染おせんといういいかたきじゃないのですが、基本きほんてき非同期ひどうき、つまりTaskでもFutureでもPromiseでもIObservableでも、は、下層かそうから上層じょうそうまで伝搬でんぱんしていきます。メソッドが非同期ひどうきであるならもどはIObservableであり、そのIObservableをぶメソッドもまた自然しぜん非同期ひどうきでなければならないので、IObservableになる、と。何故なぜ非同期ひどうき連鎖れんさでなければならないのか。消費しょうひ(Subscribe)してしまうと、その瞬間しゅんかんFire and Forgetになってしまい、もどりをったりキャンセルしたりなどのべつ操作そうさおこなえなくなってしまうからです。べつにFire and Forgetしたければ、もとがそれを選択せんたく(Subscribeして放置ほうち)すればいいわけで、ばれるがわ決定けっていすることではない。

もちろん、最終さいしゅうてきにはどこかのそう消費しょうひ(Subscribe)しなければならないので、そこで伝搬でんぱんまるのですけれど、それは、基本きほんてきには上層じょうそうであればあるほどよいということですね。どこが上層じょうそうやねんってはなしはあるかもしれませんが、ユーザーインタラクションにちかかったり、MonoBehaviourのイベントそうちかかったり、あたりがそうですかねー。あとは、ごく一部いちぶでしか使つかわないんだ!という確固かっこたるおもいがあれば、はや段階だんかいでSubscribeして伝搬でんぱんめるのもさくではあります、そのあたりはケースバイケースで。

非同期ひどうき伝搬でんぱん都合つごういメソッドが現状げんじょうのUniRxにはりてません。じつは!というわけで、次期じきバージョンではForEachAsyncというものをしたいのですが、それまでは以下いかのものをコピペって代用だいようしてください。挙動きょどうてきにはシーケンスを消費しょうひしてながさ1のIObservable[Unit]をかえすもので、もとシーケンスが非同期ひどうき(ながさ1)ならDoやSelectと、おおむ一緒いっしょです。

// 次期じきバージョンにはいるので、それまでの代用だいようということで。
// もとシーケンスが非同期ひどうきなら .Select(x => { /* action(); */ return Unit.Default; }) とほぼ同様どうよう
namespace UniRx
{
    public static class UniRxExtensions
    {
        public static IObservable<Unit> ForEachAsync<T>(this IObservable<T> source, Action<T> onNext)
        {
            return Observable.Create<Unit>(observer =>
            {
                return source.Subscribe(x =>
                {
                    try
                    {
                        onNext(x);
                    }
                    catch (Exception ex)
                    {
                        observer.OnError(ex);
                        return;
                    }
                }, observer.OnError, () =>
                {
                    observer.OnNext(Unit.Default);
                    observer.OnCompleted();
                });
            });
        }
    }
}

また、副作用ふくさようそと変数へんすうへの代入だいにゅうなど)にかんしては、あまりにしないほうがきちです。いや、Rxのパイプラインにめたほうがうつくしくはあるんですが、それがオブジェクトであるなら、副作用ふくさようかけてフィールド変数へんすうえたり、ReactivePropertyに結果けっかつたえたりとかは、あってしかりかな、と。かんがえるさいには「もしこれが同期どうきコードだったらどうなのか」を意識いしきしたほうがいいかもしれません、同期どうきコードで自然しぜんなら、べつにRxでそれをおこなっても、かまわないのです。とはいえ、以下いか紹介しょうかいするコードは全部ぜんぶ副作用ふくさよう大前提だいぜんていみたいな説明せつめいなので、それはそれで若干じゃっかん狂気きょうきでもありますが、そのあたりれてきてからでよいかと。

もどのない場合ばあい

public async Task Demo1_TaskAsync()
{
    /* before action */
    var x = await Task.Factory.StartNew(() => 100);
    /* after action */
}

public IObservable<Unit> Demo1_IOAsync()
{
    /* before action */
    return Observable.Start(() => 100)
        .ForEachAsync(_ =>
        {
            /* after action */
        });
}

メソッドにもどがない場合ばあいは、awaitの位置いちにForEachAsyncで、そのなかにactionをかたちになります。RxにおいてはIObservable[Unit]をもどのないことの表明ひょうめいとして使つかいます。

内部ないぶ複数ふくすう非同期ひどうきがある場合ばあい

public async Task Demo2_TaskAsync()
{
    /* before action */
    var x = await Task.Factory.StartNew(() => 100);
    /* after action 1 */
    var y = await Task.Factory.StartNew(() => 200);
    /* after action 2 */
}

public IObservable<Unit> Demo2_IO_1Async()
{
    /* before action */
    return Observable.Start(() => 100)
        .SelectMany(x =>
        {
            /* after action 1 */

            return Observable.Start(() => 200);
        })
        .ForEachAsync(y =>
        {
            /* after action 2 */
        });
}

awaitの位置いちにSelectManyをくことでつなげることができます。最後さいご消費しょうひだけForEachAsyncで。

パイプラインちゅう複数ふくすう伝搬でんぱんしたい場合ばあい

public IObservable<Unit> Demo2_IO_2Async()
{
    /* before action */
    return Observable.Start(() => 100)
        .SelectMany(x =>
        {
            /* after action 1 */
            return Observable.Start(() => 200);
        }, (x, y) => new { x, y }) // transport argument to next chain
        .ForEachAsync(o =>
        {
            /* after action 2 */
            // { o.x, o,y } 
        });
}

public IObservable<Unit> Demo2_IO_2_2Async()
{
    /* before action */
    return Observable.Start(() => 100)
        .SelectMany(x =>
        {
            /* after action 1 */
            var z = SyncMethod();
            return Observable.Start(() => 200).Select(y => new { x, y, z });
        })
        .ForEachAsync(o =>
        {
            /* after action 2 */
            // { o.x, o,y, o.z } 
        });
}

同期どうきコードでは、そのスコープちゅうすべての使つかえるわけですが、Rxのメソッドチェーンではつぎのパイプラインにおくめるひとつしかありません。というわけで、匿名とくめいがた(もしくはUniRx.Tuple)を使つかって、つぎのパイプラインへはをまとめてげる必要ひつようがあります。SelectManyにはだい引数ひきすうがあり、それによりまえつぎをまとめることができます。また、SelectMany内部ないぶつくったおくみたい場合ばあいは、もどのところでSelectを使つかってスコープないでキャプチャしてかえしてあげればいいでしょう。(匿名とくめいがた、Tupleともにclassなので、になる場合ばあいはstructのもの用意よういしてもいいかもしれない、なにはこつくって運搬うんぱんしなきゃいけないのは残念ざんねんながら仕様しようです)

非同期ひどうき連鎖れんさする場合ばあい

public IObservable<Unit> Demo2_IO_2_MoreChainAsync()
{
    /* before action */
    return Observable.Start(() => 100)
        .SelectMany(x =>
        {
            /* after action 1 */
            return Observable.Start(() => 200);
        }, (x, y) => new { x, y })
        .SelectMany(o =>
        {
            /* after action 2 */
            return Observable.Start(() => 300);
        }, (o, z) => new { o.x, o.y, z }) // re-construct self
        .ForEachAsync(o =>
        {
            /* after action 3 */
            // { o.x, o,y, o.z } 
        });
}

SelectManyの連打れんだになります。また、伝搬でんぱんする自分じぶん分解ぶんかいしてなおしてあげる必要ひつようがあります、これは面倒めんどうくさいですね!このあたりはクエリ構文こうぶん使つかった場合ばあい、Transparent Identifierという仕組しくみで自動的じどうてきにコンパイラがおこなうのですが(An Internal of LINQ to Objectsの35P、Rxでクエリ構文こうぶん結構けっこう頻繁ひんぱんにクエリ構文こうぶん範疇はんちゅう逸脱いつだつするのと、副作用ふくさようをパイプライン途中とちゅうけないためあまり使つか勝手がってくないので、面倒めんどうくさいながら手作業てさぎょうさい構築こうちくすすめます。

もどかえ場合ばあい

public async Task<int> Demo3_TaskAsync()
{
    /* before action */
    var x = await Task.Factory.StartNew(() => 100);
    /* after action */
    return x; // return value
}

public IObservable<int> Demo3_IOAsync()
{
    /* before action */
    return Observable.Start(() => 100)
        .Select(x =>
        {
            /* after action */
            return x; // return value
        });
}

ForEachAsyncではなく、Selectを使つかっていきましょう。もどかた同一どういつ副作用ふくさようだけこしたいならDoでもかまわないのですが、まぁどっちでもいいです。また、awaitが複数ふくすうになる場合ばあいは、SelectManyになります。そのうえでSelectManyのままreturnするか、最後さいごふたたびSelect(もしくはDo)を使つかうかどうかは、状況じょうきょう次第しだい、かな。

例外れいがいをキャッチ

public async Task Demo4_TaskAsync()
{
    /* before action */
    try
    {
        var x = await Task.Factory.StartNew(() => 100);
    }
    catch (Exception ex)
    {
        /* onerror action */
        throw;
    }

    /* after action */
}

public IObservable<Unit> Demo4_IOAsync()
{
    /* before action */
    return Observable.Start(() => 100)
        .Catch((Exception ex) =>
        {
            /* onerror action */
            return Observable.Throw<int>(ex);
        })
        .ForEachAsync(x =>
        {
            /* after action */
        });
}

これはCatchでまかなえます。なお、Catchメソッドを使つかさいは、Catch<T>で例外れいがいかた指定していするよりも、ラムダしき引数ひきすうがわ例外れいがいかたいたほうがきやすいです(そうしたほうがかた推論すいろん関係かんけいじょう、ソースシーケンスのかたかなくてむため)。CatchのもどではさいスローをObservable.Throw、にぎりつぶしをObservable.Return/Emptyで表現ひょうげん可能かのうです。

Finally

public async Task Demo5_TaskAsync()
{
    /* before action(1) */
    try
    {
        var x = await Task.Factory.StartNew(() => 100);
    }
    finally
    {
        /* finally action(2) */
    }

    /* after action(3) */
}

// not equivant try-finally
public IObservable<Unit> Demo5_IO_PseudoAsync()
{
    /* before action(1) */
    return Observable.Start(() => 100)
        .Finally(() =>
        {
            /* finally action(3) */
        })
        .ForEachAsync(x =>
        {
            /* after action(2) */
        });
}

public IObservable<Unit> Demo5_IO_CorrectLightweightButIsNotDryAsync()
{
    /* before action(1) */
    return Observable.Start(() => 100)
        .Do(_ => { /* finally action(2) */}, _ => {/* same finally action(2) */})
        .ForEachAsync(x =>
        {
            /* after action(3) */
        });
}

Finallyにかんしては、じつおなじにあつかえる表現ひょうげんがありません!RxのFinallyはパイプラインの終了しゅうりょう実行じっこうなので、実行じっこう順序じゅんじょがベタtry-finallyでいたときことなるんですよねえ。いちおう、DoでOnNextとOnErrorのところにおなじコードをくことでそれっぽい表現ひょうげん可能かのうではありますが……。

並列へいれつ処理しょり

public async Task ParallelAsync()
{
    var a = Task.Factory.StartNew(() => 100);
    var b = Task.Factory.StartNew(() => 200);
    var c = Task.Factory.StartNew(() => 300);
    
    var xs = await Task.WhenAll(a, b, c);
    /* after action */
}


public IObservable<Unit> ParallelIO()
{
    var a = Observable.Start(() => 100);
    var b = Observable.Start(() => 200);
    var c = Observable.Start(() => 300);
    
    return Observable.WhenAll(a, b, c)
        .ForEachAsync(xs =>
        {
            /* after action */
        });
}

並列へいれつ処理しょり非同期ひどうき固有こゆう実行じっこうですが、WhenAllでドバッとまとめるというのが基本きほん方針ほうしん

タイムアウト

public async Task TimeoutAsync(TimeSpan timeout)
{
    var task = Task.Factory.StartNew(() => 100);    
    var delay = Task.Delay(timeout);
    if (await Task.WhenAny(task, delay) == delay)
    {
        /* timeout action */
        throw new TimeoutException();
    }
    /* after action */
}


public IObservable<Unit> TimeoutIO(TimeSpan timeout)
{
    return Observable.Start(() => 100)
        .Timeout(timeout)
        .Catch((TimeoutException ex) =>
        {
            /* timeout action */
            return Observable.Throw<int>(ex);
        })
        .ForEachAsync(x =>
        {
            /* after action */
        });
}

タイマウトも非同期ひどうき固有こゆう処理しょり。async/awaitの場合ばあい特有とくゆうのイディオムがあります。UniRxの場合ばあいはTimeoutだけでOK。とく例外れいがい処理しょりするものもないなら、Catchは不要ふようです。

IEnumeratorにもど

public IObservable<Unit> Demo6_IE()
{
    /* before action(1) */
    return Observable.FromCoroutine(() => Demo6_IECore());
}

IEnumerator Demo6_IECore()
{
    // もど不要ふよう場合ばあい
    yield return Observable.Start(() => 100).StartAsCoroutine();
    
    int ret;
    yield return Observable.Start(() => 100).StartAsCoroutine(x => ret = x);
}

SelectManyの連打れんだつら場合ばあい、ふつーのコルーチンにもどして、さらにIObservableでラップするという手段しゅだんれます。まあ、このあたり複雑ふくざつ度合どあいで自由じゆうに!

だったらもはや最初さいしょから全部ぜんぶコルーチンでええやん!Rxでメソッドチェーン複雑ふくざつだしだけならコルーチン最強さいきょうにスッキリじゃん!というのはただしい。ただしいんですが、例外れいがい処理しょりもど合成ごうせい可能かのうせい並列へいれつ処理しょり・マルチスレッド、などといった要素ようそ欠落けつらくしてるので、コルーチンはコルーチンでくるしいところがおおいというか実際じっさいのところシンプルなケース以外いがいでは相当そうとうくるしいので、基本きほんてきにはRxのほうが有利ゆうりです。

async/awaitは必要ひつよう

みたとーり、必要ひつようです。どうかんがえても。さすがにSelectManyの連打れんだ同期どうきコードほどスッキリといいはるのは無理むりがあるでしょう。とはいえまぁ、いてけないこともないので、いまあるツールのなかでベストをくすのまたきかな、とはおもいますねー。というわけで非同期ひどうき生活せいかつを!UniRxでイベントをあつかさいのパターンしゅうは、またそのうちにでも!

Profile

Yoshifumi Kawai

Cysharp, Inc
CEO/CTO

Microsoft MVP for Developer Technologies(.NET)
April 2011
|
July 2025

X:@neuecc GitHub:neuecc

Archive