blazorからgeolocation APIを使ってみる

HTML5をサポートしているブラウザでは、GPS等を使用して、位置情報を得る、geolocation APIを使用することが可能である。

geolocation APIには以下のような関数が用意されている。

geolocation.getCurrentPosition-位置情報を1回だけ取得する。

//
//	loc:位置情報
//	err:エラー情報
//
navigator.geolocation.getCurrentPosition(
    function(loc) {}	// 位置情報取得成功時処理
    function(err) {}	// 位置情報取得失敗時処理
);

geolocation.geolocation.watchPosition-位置情報が変わるたびに指定関数を呼び出す。

//
//	loc:位置情報
//	err:エラー情報
//
watchid = navigator.geolocation.watchPosition(
    function(e) {},		// 位置情報取得成功時処理
    function(e) {}		// 位置情報取得失敗時処理
);

geolocation.clearWatch-watchPositionの停止

//
//	watchid:watchPositionの戻り値
//
navigator.geolocation.clearWatch(watchid);
位置情報パラメータ
coords 位置情報
latitude緯度
longitude経度
altitude高度
accuracy緯度・経度の誤差
altitudeAccuracy高度の誤差
heading方角(0.0~360.0)
speedスピード
timestampタイムスタンプ

エラー情報パラメータ
codeエラーコード
0 不明なエラー
1 位置情報の取得を拒否された
2 位置情報を取得できない
3 タイムアウト
messageエラーメッセージ

折角このようなAPIがあるので、blazorから呼び出してみたい。ただ、getCurrentPosition関数やwatchPosition関数のコールバック関数が必要になるので、そこから、C#コードを呼び出すようにする。

var GPSLib = {
	・・・
	GetPosition : function(helper,complete,errorhandler) {
	    navigator.geolocation.getCurrentPosition(
	        function(e) {
	            // パラメータを作成(eをそのまま渡してもキャストされないので・・・)
	            var prm = {
	                coords: {
	                    latitude: e.coords.latitude,
	                    longitude: e.coords.longitude,
	                    altitude: e.coords.altitude != null ? e.coords.altitude : 0,
	                    accuracy: e.coords.accuracy != null ? e.coords.accuracy : 0,
	                    altitudeAccuracy: e.coords.altitudeAccuracy != null ? e.coords.altitude : 0,
	                    heading: e.coords.heading != null ? e.coords.heading : 0,
	                    speed: e.coords.speed != null ? e.coords.speed : 0
	                },
	                timestamp: e.timestamp
	            };
	            // C#コードの呼出し
	            helper.invokeMethodAsync(complete,prm);
	        },
	        function(e) {
	            // パラメータを作成(eをそのまま渡してもキャストされないので・・・)
	            var prm = {
					code: e.code,
					message: e.message
				};
	            // C#コードの呼出し
				helper.invokeMethodAsync(errorhandler,prm);
	        }
	    )
	},
・・・
};
// 位置情報(一応、すべてのプロパティを得られるようにした)
public class GeoInfo {
    public Coords coords { get; set; }
    public long timestamp { get; set; }
}
public class Coords {
    public double latitude {get; set;}	// 緯度
    public double longitude {get; set;}	// 経度
    public double altitude { get; set; }	// 高度
    public double accuracy	{get; set;} // 緯度・経度の誤差
    public double altitudeAccuracy {get; set;}	// 高度の誤差
    public double heading {get; set; }	// 方角(0.0~360.0)
    public double speed  { get; set; } // スピード
}
// エラー情報
public class GeoError {
	public int code { get; set; }
	public string message { get; set; }
}

// 位置情報取得完了時処理
[JSInvokable]
public async Task OnGetPositionComplete(GeoInfo e) {
    Latitude = e.coords.latitude;
    Longitude = e.coords.longitude;
    Orient = e.coords.heading;
	・・・
    this.StateHasChanged();
}

// 位置情報取得エラー時処理
[JSInvokable]
public void OnGetPositionFail(GeoError e) {
	ErrorMessage = $"位置情報取得エラー:{e.code} - {e.message}";
	this.StateHasChanged();
}

・・・
	// GetPositionの呼出し
	await JSRuntime.InvokeVoidAsync(
	    "GPSLib.GetPosition",
	    DotNetObjectReference.Create(this),
	    "OnGetPositionComplete","OnGetPositionFail");
	);

GoogleやBing等のMAP APIと組み合わせると、スマホやタブレット用に結構面白いアプリが簡単に作成できる。(歩いた場所のトレースや現在位置からスポット検索等)

イベントの発生頻度を考えると、Server Side版よりもWASM版で作成した方が良いと思われる。(保持できるデータの上限が気になるところだが・・・)

カテゴリー: asp.net core, Blazor, C#, 技術系 | コメントする

EntityFramework上でのSqlite DateTimeの扱い

SQLiteでは、日付型というものは基本的に存在しない。

SQLのCREATE TABLE中で型としてDATETIMEを指定しても、内部的にはBinaryとなってしまい、Scaffoldした場合はbyte[]として変換されてしまう。

Entity Frameworkで、コードファーストを使用して、DateTime型のプロパティをマイグレーションすると、SQLite上にはTEXT型として作成され、自動的に相互変換をかけるように設定してくれる。

ただ、データファーストで、Scaffoldをかけた場合、TEXT型のカラムから作成されるエンティティ中のプロパティはstring型になってしまう。

TEXT型だが、内容はDateTimとして扱いたい場合は、DbContextから派生させたクラス中で、OnModelCreatingメソッドをオーバーライドして、データ変換をかけることで解決できる。

例えば、前述の日時を格納するTEXT型のカラムをDateTimeとして扱いたければ、以下のようなコードを書いておけば良い。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<MyTable>(entity =>
    {
        entity.ToTable("MyTable");

        entity.Property(e => e.PostedTime)
            .HasColumnType("TEXT")
            .HasColumnName("PostedTime")
            .HasConversion<DateTime>();
    });

    OnModelCreatingPartial(modelBuilder);
}

これにより、MyTableテーブルのPostedTimeはDB上にはTEXT型として保存され、読み出し時にはDateTimeに変換されるようになる。

SQLiteの場合、MigrationとScaffoldで結果が違うので、どうすればいいかなと調べてみた。(ちなみに、Migration時に自動的に変換定義している模様。Migrationフォルダに自動生成されているようだ。)

カテゴリー: C#, dotnetcore, Entity Framework, 技術系 | 1件のコメント

asp.net core webappでのファイルダウンロード

以前にファイルアップロードの件でも少し書いたが、asp.net core webappでのファイルダウンロードについて、多分正解というのが分かったので・・・

結局、OnPostやOnGetをvoidで定義するのではなく、下記のようにActionResultを返すメソッドとして定義する。

public IActionResult OnPost(・・・) {
}

処理中でファイルをダウンロードさせたい場合は、File(・・・)を使用して、FileResultを返せば良い。また、ファイルダウンロードでは無く、画面表示をさせたい場合は、Page()を使用して、PageResultを返す。

// Excelファイルのダウンロード
try {
	using(xls = new ExcelPackage(new FileInfo(templatepath))) {
	    // セルに対して値の設定等の処理
    ・・・
		// ちなみに、ファイル名は自動的にurlencode化されるので、
		// 事前にUrlEncodeすると変なファイル名になる。
		// PageModel.Fileを使用して、FileResultを返す
	    return File(xls.GetAsByteArray(),"application/octet-stream",filename);
	}
} catch (Exception e) {
	Message = $"エラー発生{e.Message}";
	return Page();
}

なお、File(stream,・・・)を使用する場合、Streamに書き込み等を行っていた場合、ポジションを先頭に戻さないと、実行時エラーが起きるようだ。

Stream stm = MemoryStream();
// メモリストリームに書き込み
・・・
// ポジションを先頭に
stm.Seek(0L,SeekOrigin.Begin);
return File(stm,"application/octet-stream",filename);

やっぱり、基礎を覚えないとだめだね・・・

カテゴリー: .NET, asp.net core, C#, dotnetcore, 技術系 | コメントする

asp.net webappモデルからのJS起動など

asp.net webappのモデル側からJS(例えばwindowをクローズ等)を起動できないものかと、以下のようなコードをチョット試してみた・・・

@page
@model TestModel
・・・
<script type="text/javascript">
@Html.Raw(Model.ExecScript)
</script>
public class TestModel : PageModel {
  ・・・
  public string ExecScript;
  public void OnPost(・・・) {
    ・・・
    ExecScript = @"window.close();";
    ・・・
}

結論としてはうまく動作。ただ、セキュリティ上Html.Rawはあまり使うべきではないのかな?

ちなみに、gijgoのTreeViewのdataSource指定なんかにもHtml.Rawを使って動的にTreeの内容を変更することも出来たけど、他にいいやり方あるかな?

<script type="text/javascript">
    $(document).ready(function() {
        $('#tree').tree({
            uiLibrary: 'bootstrap4',
            dataSource: @Html.Raw(Model.TreeItems), /* データソース(JSON)はモデルで作成 */
            primaryKey: 'id',
            imageUrlField: 'flagUrl'
        });
    });
カテゴリー: .NET, asp.net core, C#, 技術系 | 1件のコメント

.net core,.net 5でのShift_JISエンコーディング

最近、SJISのテキストファイルを.net 5でいじる必要があって、忘れていたことがあったので、メモ。

.net coreや.net 5では、デフォルトのエンコーディングセットとして、Shift_JISはサポートされていない。

CodePage=1200, Name=utf-16 ,DisplayName=Unicode
CodePage=1201, Name=utf-16BE ,DisplayName=Unicode (Big-Endian)
CodePage=12000, Name=utf-32 ,DisplayName=Unicode (UTF-32)
CodePage=12001, Name=utf-32BE ,DisplayName=Unicode (UTF-32 Big-Endian)
CodePage=20127, Name=us-ascii ,DisplayName=US-ASCII
CodePage=28591, Name=iso-8859-1 ,DisplayName=Western European (ISO)
CodePage=65001, Name=utf-8 ,DisplayName=Unicode (UTF-8)

Shift_JISやEUCなどを使用したい場合は、System.Text.Encoding.CodePagesパッケージが必要となる。

> dotnet add <プロジェクトフォルダ> package System.Text.Encoding.CodePages

これをプロジェクトに追加するだけではダメで、Encododingを取得する以前にプログラム中で下記処理を実行することが必要となる。

Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

これで、GetEncoding(“Shift_JIS”)でShift_JISエンコーディングを取得することが可能となる。ちなみに、エンコーディングリストを見てみたのだが、ISO-2022-JPが無いようにみえるのだが・・・まぁ、メール以外には使わないけど。

CodePage=37, Name=IBM037 ,DisplayName=IBM037
CodePage=437, Name=IBM437 ,DisplayName=IBM437
CodePage=500, Name=IBM500 ,DisplayName=IBM500
CodePage=708, Name=ASMO-708 ,DisplayName=ASMO-708
CodePage=720, Name=DOS-720 ,DisplayName=DOS-720
CodePage=737, Name=ibm737 ,DisplayName=ibm737
CodePage=775, Name=ibm775 ,DisplayName=ibm775
CodePage=850, Name=ibm850 ,DisplayName=ibm850
CodePage=852, Name=ibm852 ,DisplayName=ibm852
CodePage=855, Name=IBM855 ,DisplayName=IBM855
CodePage=857, Name=ibm857 ,DisplayName=ibm857
CodePage=858, Name=IBM00858 ,DisplayName=IBM00858
CodePage=860, Name=IBM860 ,DisplayName=IBM860
CodePage=861, Name=ibm861 ,DisplayName=ibm861
CodePage=862, Name=DOS-862 ,DisplayName=DOS-862
CodePage=863, Name=IBM863 ,DisplayName=IBM863
CodePage=864, Name=IBM864 ,DisplayName=IBM864
CodePage=865, Name=IBM865 ,DisplayName=IBM865
CodePage=866, Name=cp866 ,DisplayName=cp866
CodePage=869, Name=ibm869 ,DisplayName=ibm869
CodePage=870, Name=IBM870 ,DisplayName=IBM870
CodePage=874, Name=windows-874 ,DisplayName=windows-874
CodePage=875, Name=cp875 ,DisplayName=cp875
CodePage=932, Name=shift_jis ,DisplayName=shift_jis
CodePage=936, Name=gb2312 ,DisplayName=gb2312
CodePage=949, Name=ks_c_5601-1987 ,DisplayName=ks_c_5601-1987
CodePage=950, Name=big5 ,DisplayName=big5
CodePage=1026, Name=IBM1026 ,DisplayName=IBM1026
CodePage=1047, Name=IBM01047 ,DisplayName=IBM01047
CodePage=1140, Name=IBM01140 ,DisplayName=IBM01140
CodePage=1141, Name=IBM01141 ,DisplayName=IBM01141
CodePage=1142, Name=IBM01142 ,DisplayName=IBM01142
CodePage=1143, Name=IBM01143 ,DisplayName=IBM01143
CodePage=1144, Name=IBM01144 ,DisplayName=IBM01144
CodePage=1145, Name=IBM01145 ,DisplayName=IBM01145
CodePage=1146, Name=IBM01146 ,DisplayName=IBM01146
CodePage=1147, Name=IBM01147 ,DisplayName=IBM01147
CodePage=1148, Name=IBM01148 ,DisplayName=IBM01148
CodePage=1149, Name=IBM01149 ,DisplayName=IBM01149
CodePage=1200, Name=utf-16 ,DisplayName=Unicode
CodePage=1201, Name=utf-16BE ,DisplayName=Unicode (Big-Endian)
CodePage=1250, Name=windows-1250 ,DisplayName=windows-1250
CodePage=1251, Name=windows-1251 ,DisplayName=windows-1251
CodePage=1252, Name=windows-1252 ,DisplayName=windows-1252
CodePage=1253, Name=windows-1253 ,DisplayName=windows-1253
CodePage=1254, Name=windows-1254 ,DisplayName=windows-1254
CodePage=1255, Name=windows-1255 ,DisplayName=windows-1255
CodePage=1256, Name=windows-1256 ,DisplayName=windows-1256
CodePage=1257, Name=windows-1257 ,DisplayName=windows-1257
CodePage=1258, Name=windows-1258 ,DisplayName=windows-1258
CodePage=1361, Name=Johab ,DisplayName=Johab
CodePage=10000, Name=macintosh ,DisplayName=macintosh
CodePage=10001, Name=x-mac-japanese ,DisplayName=x-mac-japanese
CodePage=10002, Name=x-mac-chinesetrad ,DisplayName=x-mac-chinesetrad
CodePage=10004, Name=x-mac-arabic ,DisplayName=x-mac-arabic
CodePage=10005, Name=x-mac-hebrew ,DisplayName=x-mac-hebrew
CodePage=10006, Name=x-mac-greek ,DisplayName=x-mac-greek
CodePage=10007, Name=x-mac-cyrillic ,DisplayName=x-mac-cyrillic
CodePage=10010, Name=x-mac-romanian ,DisplayName=x-mac-romanian
CodePage=10017, Name=x-mac-ukrainian ,DisplayName=x-mac-ukrainian
CodePage=10021, Name=x-mac-thai ,DisplayName=x-mac-thai
CodePage=10029, Name=x-mac-ce ,DisplayName=x-mac-ce
CodePage=10079, Name=x-mac-icelandic ,DisplayName=x-mac-icelandic
CodePage=10081, Name=x-mac-turkish ,DisplayName=x-mac-turkish
CodePage=10082, Name=x-mac-croatian ,DisplayName=x-mac-croatian
CodePage=12000, Name=utf-32 ,DisplayName=Unicode (UTF-32)
CodePage=12001, Name=utf-32BE ,DisplayName=Unicode (UTF-32 Big-Endian)
CodePage=20000, Name=x-Chinese-CNS ,DisplayName=x-Chinese-CNS
CodePage=20001, Name=x-cp20001 ,DisplayName=x-cp20001
CodePage=20002, Name=x-Chinese-Eten ,DisplayName=x-Chinese-Eten
CodePage=20003, Name=x-cp20003 ,DisplayName=x-cp20003
CodePage=20004, Name=x-cp20004 ,DisplayName=x-cp20004
CodePage=20005, Name=x-cp20005 ,DisplayName=x-cp20005
CodePage=20105, Name=x-IA5 ,DisplayName=x-IA5
CodePage=20106, Name=x-IA5-German ,DisplayName=x-IA5-German
CodePage=20107, Name=x-IA5-Swedish ,DisplayName=x-IA5-Swedish
CodePage=20108, Name=x-IA5-Norwegian ,DisplayName=x-IA5-Norwegian
CodePage=20127, Name=us-ascii ,DisplayName=US-ASCII
CodePage=20261, Name=x-cp20261 ,DisplayName=x-cp20261
CodePage=20269, Name=x-cp20269 ,DisplayName=x-cp20269
CodePage=20273, Name=IBM273 ,DisplayName=IBM273
CodePage=20277, Name=IBM277 ,DisplayName=IBM277
CodePage=20278, Name=IBM278 ,DisplayName=IBM278
CodePage=20280, Name=IBM280 ,DisplayName=IBM280
CodePage=20284, Name=IBM284 ,DisplayName=IBM284
CodePage=20285, Name=IBM285 ,DisplayName=IBM285
CodePage=20290, Name=IBM290 ,DisplayName=IBM290
CodePage=20297, Name=IBM297 ,DisplayName=IBM297
CodePage=20420, Name=IBM420 ,DisplayName=IBM420
CodePage=20423, Name=IBM423 ,DisplayName=IBM423
CodePage=20424, Name=IBM424 ,DisplayName=IBM424
CodePage=20833, Name=x-ebcdic-koreanextended ,DisplayName=x-ebcdic-koreanextended
CodePage=20838, Name=IBM-Thai ,DisplayName=IBM-Thai
CodePage=20866, Name=koi8-r ,DisplayName=koi8-r
CodePage=20871, Name=IBM871 ,DisplayName=IBM871
CodePage=20880, Name=IBM880 ,DisplayName=IBM880
CodePage=20905, Name=IBM905 ,DisplayName=IBM905
CodePage=20924, Name=IBM00924 ,DisplayName=IBM00924
CodePage=20932, Name=EUC-JP ,DisplayName=EUC-JP
CodePage=20936, Name=x-cp20936 ,DisplayName=x-cp20936
CodePage=20949, Name=x-cp20949 ,DisplayName=x-cp20949
CodePage=21025, Name=cp1025 ,DisplayName=cp1025
CodePage=21866, Name=koi8-u ,DisplayName=koi8-u
CodePage=28591, Name=iso-8859-1 ,DisplayName=Western European (ISO)
CodePage=28592, Name=iso-8859-2 ,DisplayName=iso-8859-2
CodePage=28593, Name=iso-8859-3 ,DisplayName=iso-8859-3
CodePage=28594, Name=iso-8859-4 ,DisplayName=iso-8859-4
CodePage=28595, Name=iso-8859-5 ,DisplayName=iso-8859-5
CodePage=28596, Name=iso-8859-6 ,DisplayName=iso-8859-6
CodePage=28597, Name=iso-8859-7 ,DisplayName=iso-8859-7
CodePage=28598, Name=iso-8859-8 ,DisplayName=iso-8859-8
CodePage=28599, Name=iso-8859-9 ,DisplayName=iso-8859-9
CodePage=28603, Name=iso-8859-13 ,DisplayName=iso-8859-13
CodePage=28605, Name=iso-8859-15 ,DisplayName=iso-8859-15
CodePage=29001, Name=x-Europa ,DisplayName=x-Europa
CodePage=65001, Name=utf-8 ,DisplayName=Unicode (UTF-8)
カテゴリー: .NET, dotnetcore, 技術系 | コメントする

asp.net coreでのファイルアップロード

asp.net core webappでローカルファイルをアップロードするには、一般的なHTMLと同様、formタグにenctype=”multipart/form-data”を付け、<input type=”file”>を使用して、ファイル本体をアップロードする。

<form enctype="multipart/form-data" method="post">
	・・・
	アップロードファイル:<input type="file" name="fupFile"/>
	<button class="btn btn-primary" type="submit">アップロード</button>
</form>

受取側は、IFormFileクラスを指定して、引数として受取ることが可能。

public async Task OnPost([FromForm]IFormFile fupFile) {

    ・・・
    Stream stm = fupFile.OpenReadStream();
	// ファイルに対する処理
	・・・
}

まだ試していないが、Blazorの場合は少々異なり、<InputFile>タグとそのイベントハンドラで処理ができるようだ。

・・・
<InputFile OnChange="@OnInputFileChange"/>
・・・
@code {
	・・・
	protected void OnInputFileChange(InputFileChangeEventArgs e) {
		Stream stm = e.File.OpenReadStream();
		// ファイル処理
	}
}

ちなみに、webapp中のハンドラで動的に作成したファイルのダウンロードだが、ASP.NETで使っていた方法では、ミドルウェアのせいで実行時エラー(Responseヘッダは上書き禁止)が出て、動作しなかった。

色々試してみて、結局MVC(FileContentResultを返す)を一部使用することによってダウンロードできるようになったが、他に方法はないものだろうか・・・

@{
	Dictionary<string,string> prm = new Dictionary<string,string>() {
		{"Query" : Model.Query}
	};
}
・・・
<button class="btn btn-primary" asp-controller="[コントローラ]" asp-action="Download" asp-asp-all-route-data="prm">ダウンロード</button>
public async Task<FileContentResult> Download(string Query) {
	・・・
	MemoryStream stm = new MemoryStream();
	// MemoryStreamにファイルを作成する処理
	・・・
	byte[] buff = stm.ToArray();
    return new FileContentResult(
        buff,
        "application/octet-stream"
    ) {
        FileDownloadName = filename
    };
}

カテゴリー: asp.net core, Blazor, 技術系 | 5件のコメント

dotnet core Worker ServiceのWindows Service化

dotnet core(.NET 5も含む)のWorker ServiceをWindowsのServiceとして動作させる例を見つけたのでメモ。

Worker Serviceは下記コマンドで作成する。

dotnet new worker -o <プロジェクト名>

通常は、上記によって作成されたWorker.csに処理を付け加えればOKなのだが、Windows Service化させたい場合は、プロジェクトにMicrosoft.Extensions.Hosting.WindowsServicesパッケージを追加する必要がある。

dotnet add <プロジェクト名> package Microsoft.Extensions.Hosting.WindowsServices

さらに、Program.cs中のCreateHostBuilderに.UseWindowsService()を追加する。

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseWindowsService()
        .ConfigureServices((hostContext, services) =>
        {
            services.AddHostedService<Worker>();
        });
}

後は、Worker.csに処理を追加して、デバッグ後、下記コマンドでサービス登録を行う。(これはWindowsのコマンド)

sc create <サービス名> binPath= 実行ファイルパス [オプション...]

登録が済めば、MMCコンソールの「コンピューターの管理」-「サービスとアプリケーション」-「サービス」からも起動・停止や設定変更が可能。

単純なサービスの開始や停止は下記コマンドでも操作可能。

sc start <サービス名>
sc stop <サービス名>

.NET Framework時代のサービス作成より楽だね。

カテゴリー: .NET, C#, dotnetcore, 技術系 | コメントする

.NET 5のターゲットフレームワーク名

現地時間11/10 .NET 5がリリースされた。

.NET 5では、フレームワーク名にプラットフォームを指定出来るようになった。プロジェクト種別によっては、プラットフォームを指定しないとエラーになるものもある。なお、ターゲットフレームワークは;で区切ることにより複数指定が可能である。

例えば、Windows Formsプロジェクトでは、単なるnet5.0の指定だけでは、プラットフォームエラーが出てBuildに失敗する。.NET 5で作成したプロジェクトでは、問題無いが、.net core 3.1で作成したプロジェクトを.NET 5にアップグレードする場合等には注意が必要だ。

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net5.0-windows</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
  </PropertyGroup>

また、プラットフォームにOSやOSバージョンを指定する事により、プログラムから、それらの値を取得することも可能。

.NET 5でサポートされている、プラットフォームは以下のようになっている。

プラットフォームバージョン指定可能
net5.0-androidYes
net5.0-iosYes
net5.0-macosYes
net5.0-tvosYes
net5.0-watchosYes
net5.0-windowsNo

※プロジェクトがクロスプラットフォームの場合は単にnet5.0で良い。

カテゴリー: .NET, C#, 技術系 | コメントする

EF CoreによるDBからのEntity作成(Scaffolding)

以前、少し書いたと思うが、Entity Framework Coreを使用して、既存のDBからDbContextクラスとEntityクラスを作成する事が可能である。

例えば以下のようなテーブルを含むDBがあった場合、どのようなEntityおよびDbContextとなるか試してみよう。

データベース(SQLite)

CREATE TABLE Person(
  Id int atuoincrement,
  Name nvarchar(50),
  primary key (Id)
);
CREATE TABLE MailAddress(
  Address nvarchar(100),
  DisplayName nvarchar(50),
  PersonId int,
  primary key(Address),
  foreign key(PersonId) references Person(Id)
);

プロジェクトには下記のパッケージを追加する

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameoworkCore.Design
  • Microsoft.EntityFrameworkCore.Sqlite
  • Microsoft.EntityFrameowrkCore.Sqlite.Design

下記コマンドを実行して、DbContextとEntityを作成
(scaffoldのパラメータは「接続文字列」と「データプロバイダ」)

$ dotnet ef dbcontext scaffold "Data Source=SQLiteTest.db" Microsoft.EntityFrameworkCore.Sqlite

出来上がったDbContextとEntity

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;

namespace SQLiteTest
{
    public partial class SQLiteTestContext : DbContext
    {
        public SQLiteTestContext()
        {
        }

        public SQLiteTestContext(DbContextOptions<SQLiteTestContext> options)
            : base(options)
        {
        }

        public virtual DbSet<MailAddress> MailAddress { get; set; }
        public virtual DbSet<Person> Person { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.
                optionsBuilder.UseSqlite("Data Source=SQLiteTest.db");
            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<MailAddress>(entity =>
            {
                entity.HasKey(e => e.Address);

                entity.Property(e => e.Address).HasColumnType("nvarchar(100)");

                entity.Property(e => e.DisplayName).HasColumnType("nvarchar(50)");

                entity.Property(e => e.PersonId).HasColumnType("int");

                entity.HasOne(d => d.Person)
                    .WithMany(p => p.MailAddress)
                    .HasForeignKey(d => d.PersonId);
            });

            modelBuilder.Entity<Person>(entity =>
            {
                entity.Property(e => e.Id)
                    .HasColumnType("int atuoincrement")
                    .ValueGeneratedNever();

                entity.Property(e => e.Name).HasColumnType("nvarchar(50)");
            });

            OnModelCreatingPartial(modelBuilder);
        }

        partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
    }
}
using System;
using System.Collections.Generic;

namespace SQLiteTest
{
    public partial class Person
    {
        public Person()
        {
            MailAddress = new HashSet<MailAddress>();
        }

        public long Id { get; set; }
        public string Name { get; set; }

        public virtual ICollection<MailAddress> MailAddress { get; set; }
    }
}
using System;
using System.Collections.Generic;

namespace SQLiteTest
{
    public partial class MailAddress
    {
        public string Address { get; set; }
        public string DisplayName { get; set; }
        public long? PersonId { get; set; }

        public virtual Person Person { get; set; }
    }
}

とまあ、こんな感じでDBからDbContextとEntityを作成することができる。
接続文字列が埋め込みになっているので、この部分はappsettings.json等から取得するように変更した方が良いだろう。

なお、scaffoldのオプションで、scaffoldするテーブルを指定したり、出力先ディレクトリを指定することも可能。

カテゴリー: C#, dotnetcore, Entity Framework, 技術系 | コメントする

Windows TerminalでSSH接続プロファイルを作成してみる

Windows Terminalとは、Mircosoft Storeで入手できる、Microsoft社製の無料のTerminalアプリケーション。

インストールすると、デフォルトで、Powershellとコマンドプロンプトおよび、wslのディストリビューションがインストールされていれば、そのプロファイルが自動追加され、メニューより選択してコンソールを開く事が可能となる。

ちなみに、メニューから「設定」を選ぶと、所謂設定画面ではなく、jsonファイルが拡張子が関連づけられたエディタで開く。このファイルを編集することにより、プロファイル毎に背景色を変更したりすることが可能である。

ただ、自動的に設定されるのは、ローカルリソースのみなので、手動にてSSHでリモートに接続するプロファイルを作成してみた。

Windows10では2017年10月のFall Creators Updateからsshクライアントが標準でサポートされているので、新しいプロファイルを作成して、起動コマンドとして、”commandline”にsshを指定してあげれば良い。

{
    "$schema": "https://aka.ms/terminal-profiles-schema",
    "alwaysShowTabs" : true,
	・・・
    "profiles" : 
    [
		・・・
        {
            "acrylicOpacity" : 0.8,
            "background" : "#102040",
            "closeOnExit" : true,
            "colorScheme" : "Campbell",
            "commandline" : "ssh <username>@<host>",←SSHでリモートホストに接続
            "cursorColor" : "#FFFFFF",
            "cursorShape" : "bar",
            "fontFace" : "Consolas",
            "fontSize" : 16,
            "guid" : "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}",←GUIDジェネレータ等で取得
            "historySize" : 9001,
            "icon" : "ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png",
            "name" : "SSH-Connect(<host>)",
            "padding" : "0, 0, 0, 0",
            "snapOnInput" : true,
            "useAcrylic" : true
        },
		・・・
	]
	・・・
}

(←・・・の部分は実際には書いてはいけない)

上記を保存して、メニューからそのプロファイルを選択してみる。

見事ログインできましたとさ。

今までは、Teratermを使っていたけれど、このアプリ1個開いておけばCMDやPowershellなどとの操作切替が楽なので、今後はこれを使おうかな。

ちなみに、同じプロファイルのタブを複数開くことも可能です。

カテゴリー: Windows, 技術系, 開発環境 | コメントする