[.net core 3.0] Linux用のCliツール作成(単一ファイル)

新しいプロジェクトの作成

コンソールアプリ(.NET Core) -> プロジェクト名(ここでは適当にTestTool)

code

using System;

namespace TestTool {
    class Program {
        static void Main(string[] args) {
            Console.WriteLine("Hello World!");
            foreach (var arg in args) {
                Console.WriteLine(arg);
            }
            /* end */
            Console.WriteLine("Press Any key to continue...");
            Console.ReadKey(true);
        }
    }
}

発行のプロファイルを作る

ビルド -> TestToolの発行 -> 発行先はフォルダ -> プロファイルの作成

配置モードを自己完結, ターゲットランタイムをlinux-x64に変更して保存。

単一ファイルに変更するためにprofileファイルを編集。ソリューションエクスプローラからProperties>PublishProfiles>FolderProfile.pubxmlを選択して開く。

<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121. 
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <PublishProtocol>FileSystem</PublishProtocol>
    <Configuration>Release</Configuration>
    <Platform>Any CPU</Platform>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <PublishDir>bin\publish\</PublishDir>
    <RuntimeIdentifier>linux-x64</RuntimeIdentifier>
    <SelfContained>true</SelfContained>
    <PublishSingleFile>true</PublishSingleFile> <!--単一ファイル出力指定-->
  </PropertyGroup>
</Project>

PublishSingleFileを追加して保存。

発行

ビルド -> TestToolの発行 -> 発行

実行

単一ファイルと言いつつpdbファイル(Releaseビルドなのに)も出力される。作成されたlinuxバイナリ(TestTool)(76MB)をこのサーバにアップロードして実行。

[dobu@133-130-123-5 ~]$ chmod +x TestTool
[dobu@133-130-123-5 ~]$ ./TestTool hoge fuga piyo
Hello World!
hoge
fuga
piyo
Press Any key to continue...
[dobu@133-130-123-5 ~]$

明るくないsh書くよりこっちのほうが楽だなー。個人では複雑怪奇なことをlinuxでやることはないのだけど、debuggerが強力なので。単一ファイルにすることで各バージョン気にしなくてすむのが最高。ファイルサイズの肥大化は……このご時世ある程度までなら増えても別にいいだろって。

ex) https://docs.microsoft.com/ja-jp/dotnet/core/whats-new/dotnet-core-3-0#single-file-executables

[.net framework] windows.formsの2重起動禁止

表題の件だけど、こんなサンプルコードが多く見られる。

上記は動作はするけどダメな例だ。Mutexはdisposableなのだし、Local変数にするとGCが葬ってしまう可能性がある。こう書くの。

ネットに転がってるサンプルを鵜呑みにして実装しちゃだめだなーって。誰も見ないんだけど自戒の念を込めて。

[.net framework] chromium web browser

久々にWindows.Formsです。本当は.net coreで作りたかったのだけど、WebBrowserコントロールがIE7なのな!さすがにEdgeだとは思っていないがIE11かと……。ということで埋め込みchromiumコントロールを使うことにした。これが.net core3.0ではどうにもこうにもエラーになるので、今回は.net framework 4.7.2で。

https://gist.github.com/dobusarai2016/9e3d179d8f5ac7fa46468b468d6e8616

マニフェスト追加して高DPI対応とかにもしてるけど、基本はこれだけ。ボタンとかはさすがに面倒なのでフォームデザイナでペタペタ。CefSharpはデザイナで貼るとバージョンが古いだのビルド時にようわからんエラー吐いたりだの鬱陶しいので直書き。

Application -> Chromiumは基本的にJavascriptを走らせることでDOM操作とか何でも出来るみたいだが、Chromium -> Application、例えばDOMの取得などはレンダリング時にしかできないっぽい。まー目的は片方向で良いので調べるのはやめた。生スレッド使うのはダセえとかは言うな。

※20191017 生スレッドの部分を修正

※20191017 LoadError(httpdが存在しないページで404を返すのではなく、サーバ自体に接続できない、存在しない場合は真っ白で終わり)時に、エラーhtmlを読み込んで描画するように変更

※20191017 DOM要素へのアクセスも試してみた。が、idやインデックスを指定しての取得は出来るが、複数の要素(getElementsByClassNameでまとめて取得)はdebuggerで見てもnullになってしまって取れないみたい。

        private async void ToolStripButton2_Click(object sender, EventArgs e)
        {
            if (!chromium.IsLoading)
            {
                //var js = "document.getElementsByClassName('chromium-test');"; // NG
                //var js = "document.getElementsByClassName('chromium-test')[0];"; // NG
                var js = "document.getElementsByClassName('chromium-test')[0].innerText;"; // OK
                //var js = "document.getElementById('user_id').value;"; // OK
                var test = "";
                await chromium.GetBrowser().MainFrame.EvaluateScriptAsync(js).ContinueWith(
                    x => {
                        var response = x.Result;
                        if(response.Success && response.Result != null)
                        {
                            test = response.Result.ToString();
                        }
                    }
                );
                StatusMessageLabel.Text = test;
            }
        }

https://www.dobusarai.net/env/

AWS lambda C# & dynamodb ローカル開発環境

ゴールデンウィークなので、表題の件でもやるよ。Visual Studio 2019は適当にインストールされていることが前提だが、アホでもdynamodbにput/get出来るまで書くよ。とりあえず俺は、"ASP.NET と Web 開発", ".NET デスクトップ開発", ".NET Core クロスプラットフォームの開発"の3つを初期インストールで入れているが、後にゲームやりたくなったりなんだりで、違うことやりたくてもVisual Studio Installerで追加できるので問題はないぞ。aws cliは(これ見る人は入れていると思うけど)入っていることが前提。俺はこれ( https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-windows.html )のインストーラ使って入れてます。Python嫌いなので。

C:\>aws configure
AWS Access Key ID [None]: dummy
AWS Secret Access Key [None]: dummy
Default region name [None]: ap-northeast-1
Default output format [None]: json

テストでも必要になるので適当に設定。

amazonさんが提供するlocal用のdynamodbなのだけど、JRE(JDK)が必要なのです。俺は入れた覚えが全く無いのだが、下記バージョンが入っていた。ココらへんはawsのドキュメント見てくれ。あーーーんど、dynamodbの実行ファイル(jar)もあるのでダウンロードしてね。 https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html

下記は俺の環境。

E:\>java -version
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b18)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b18, mixed mode)

とりあえずE:に配置。コマンド面倒くさいのでバッチで書いて実行。

E:\dynamodb_local>start.bat

E:\dynamodb_local>java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb
Initializing DynamoDB Local with the following configuration:
Port:   8000
InMemory:       false
DbPath: null
SharedDb:       true
shouldDelayTransientStatuses:   false
CorsParams:     *

適当なテーブルとデータを作成してみましょうか。WindowsのCLIだとPythonのクソ野郎がエラー吐きまくる(let's encryptのcertbotで、nginxのconfig commentに2byte文字使った時と同じ)ので、ここでは日本語使いません。コマンドプロンプトをもう一窓開いて実行。

C:\>aws dynamodb create-table --table-name Posts --attribute-definitions AttributeName=Id,AttributeType=N AttributeName=Date,AttributeType=S --key-schema AttributeName=Id,KeyType=HASH AttributeName=Date,KeyType=RANGE --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 --endpoint-url http://localhost:8000
{
    "TableDescription": {
        "AttributeDefinitions": [
            {
                "AttributeName": "Id",
                "AttributeType": "N"
            },
            {
                "AttributeName": "Date",
                "AttributeType": "S"
            }
        ],
        "TableName": "Posts",
        "KeySchema": [
            {
                "AttributeName": "Id",
                "KeyType": "HASH"
            },
            {
                "AttributeName": "Date",
                "KeyType": "RANGE"
            }
        ],
        "TableStatus": "ACTIVE",
        "CreationDateTime": 1556655253.192,
        "ProvisionedThroughput": {
            "LastIncreaseDateTime": 0.0,
            "LastDecreaseDateTime": 0.0,
            "NumberOfDecreasesToday": 0,
            "ReadCapacityUnits": 1,
            "WriteCapacityUnits": 1
        },
        "TableSizeBytes": 0,
        "ItemCount": 0,
        "TableArn": "arn:aws:dynamodb:ddblocal:000000000000:table/Posts",
        "BillingModeSummary": {
            "BillingMode": "PROVISIONED",
            "LastUpdateToPayPerRequestDateTime": 0.0
        }
    }
}

ここでputだのなんだの実行したいところですが、WindowsのCLIだとjson構文でエラー頻発するのでスルーします。bash on ubuntu on windowsだとか、git bushならうまくいくかもしれませんね。うまくいくんですが。とにかくPython嫌いなので先に進みます。コマンド例は下記。

// create table パーティションキー(HASH), ソートキー(RANGE)
aws dynamodb create-table --table-name Posts --attribute-definitions AttributeName=Id,AttributeType=N AttributeName=Date,AttributeType=S --key-schema AttributeName=Id,KeyType=HASH AttributeName=Date,KeyType=RANGE --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 --endpoint-url http://localhost:8000

// table 一覧
aws dynamodb list-tables --endpoint-url http://localhost:8000

// table 詳細
aws dynamodb describe-table --table-name Posts --endpoint-url http://localhost:8000

// insert test data
aws dynamodb put-item --table-name Posts --item '{"Id":{"N":"1"},"Date":{"S":"20190419"},"Name":{"S":"Ayana Taketatsu"}}' --endpoint-url http://localhost:8000

// get item
aws dynamodb get-item --table-name Posts --key '{"Id":{"N":"1"}}' --endpoint-url http://localhost:8000

// get all item (scan)
aws dynamodb scan --table-name Posts --endpoint-url http://localhost:8000

ここからVisual Studioで新しいプロジェクトを作成。まずはブランクで起動(コード無しで続行)し、メニューの拡張機能のオンラインから"AWS Toolkit for Visual Studio 2017 and 2019"を入れる。入れたら閉じろ、だのダイアログが出たりなんだりするが指示通りで問題ない。ここらへんはnodeなんかと違って非常に親切なのでそのまま従う。で、終わったのなら早速Lambdaプロジェクトを作成する。新しいプロジェクトの作成->"AWS Lambda Project (.NET Core - C#)"だ。with Testsを選んでもいいが解説はしない。適当にディレクトリなどを指定して進む。AWS SDKが雛形を作る?とか聞いてくるが無視してemptyで作る。

メニューの表示->AWS Explorerで左窓を開いて、Edit ProfileのIDとKeyを"dummy"にしてOK。(上の方で設定したaws cliの値)

ソリューションエクスプローラから、ソリューションを右クリックして"ソリューションのNuGetパッケージの管理"を選択。検索するなりして"AWSSDK.DynamoDBv2"をプロジェクトにインストール。

ソリューションエクスプローラで"aws-lambda-tools-defaults.json"を開きリージョンだけ変更する。

{
  "Information" : [
    "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.",
    "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.",

    "dotnet lambda help",

    "All the command line options for the Lambda command can be specified in this file."
  ],

  "profile":"default",
  "region" : "ap-northeast-1",
  "configuration" : "Release",
  "framework" : "netcoreapp2.1",
  "function-runtime":"dotnetcore2.1",
  "function-memory-size" : 256,
  "function-timeout" : 30,
  "function-handler" : "AWSLambda1::AWSLambda1.Function::FunctionHandler"
}

本来なら複数クラスに分割するところですが、適当に3つのクラスにすべて記述する。ソリューションエクスプローラから"PostsEntity"クラスを新規作成。Function.cs, Status.csは下記。

PostEntity.cs

Function.cs

Status.cs

(20190501変更) ソースコードがうまく貼れないのでgithubに。
https://github.com/dobusarai2016/20190501lambda

(20190514) ソースと結果を読み返してみたらJsonっぽい文字列を返すというわけのわからんコードになっていますね……。FunctionHandlerはstringではなくClass Objectを返すようにするとLambdaSerializerが良きに計らってくれます。近日中に修正します。

うーーーーん、言語別に綺麗に整形するようにしたいなこれ。で、下のようにjsonが返ってくるのを確認。

データ例。

// Function Input:
# get
{
	"Id": 1,
	"Date":"20190501",
	"Method":"get"
}
# save
{
	"Id": 110,
	"Date":"20190501",
	"Name":"魑魅魍魎",
	"Method":"save"
}
# delete
{
	"Id": 110,
	"Date":"20190501",
	"Method":"delete"
}
# scan
{
	"Method":"scan"
}

DynamoというかNoSQLでの設計が未だによくわかっていないのですが、単なるKVSに見えるのでそもそもRDBMSみたいな使い方ではなくCMSのデータ置き場みたいに使うのかなこれ。簡単な業務アプリならいけそうだと思っていたのだけど検索難しいなー。

あ、Visual StudioさんはそのままLambdaをAWSに配置したりする機能があるようなのですが、個人ではアカウントを所有していないので試していません。連休明けに会社行ったら試そうかな。

Visual Studio 2019

新登場したVisual Studio 2019のニュースを見て、いつ触ろうかと思っていたのだが想像より早く手を出すことになった。

AWS lambda (API gateway) & dynamodbをgolangで書いていたのだが、LocalPCでのdebug環境がなかなか整わなくてイライラしていた。aws cliはpython製のようなのだが、ドザ特有の現象(setlocale?)なのかやたらつまづきやがる。あー、これlet's encrypt(certbot)でも出たやつだわー。あー、めんどくせー。みたいな。あ、aws cli自体は入るのだけどaws-sam-cliとかいうのが必要らしいのです。

俺はコードを書くのは好きだし、何ならアホがコアハックしてにっちもさっちもいかない腐ったコード(昔はMFCとか最近だとmagentoとか)を何とかまともに改修するのも好きだ(嫌だ)。けれど、環境構築だとか、わけのわからんミドルウェアのおもりだとかは大嫌い。なので、ruby on rails, Java(特に昔の泥はひどかった), etc が大嫌い。まあ俺がドザってのもあるんだろうけれども。仕事でメインにしているPHPも、XAMPP & xdebug & PhpStormで構築は楽勝。過去メインで使っていたC#はいわずもがな。

で、Visual Studio 2019とAWS SDKをインストールして上記のLocal環境を作ってみたが、2時間もかからずにJSON突っ込んでDynamoDB(Amazonさんが提供しているJarファイル)にアクセスして結果出力までできた。当然、強力なdebuggerも使用できる。

そこで興味が出てきたので、先程までAzureについて見ていた。AWS以外はさっぱりわからないのだけど、Azureは完全無料でできる方法もあるみたい。マジかー。あとさー、Community版のこの記述。
https://visualstudio.microsoft.com/ja/vs/community/

個人開発者の方はどなたでも、独自の無料/有料のアプリを作成するために Visual Studio Community を使用できます。
エンタープライズ以外の組織では最大 5 ユーザーで Visual Studio Community を使用できます。

またまたマジかー。実質無料じゃん。昔、副業のために12万くらい出してPro版買ってたのに。最近のMSさんは何かすごいなー。かーっ、C#ずっと書いていてえなあ!(まあ、脳内はかなり古いC#(3.0とか?)で止まっているので早く習得しろって話だが)