C# 7 のタプル機能(そろそろC# 7について考えたのです)

C# 7 ではいくつかの機能追加が行われています。
というか、言語仕様レベルでの機能追加がまだ行われるとか、どんどん複雑化していくような危惧も持ってしまいますが・・・「デコンストラクタ」とか本当に必要なのかなぁ・・・とか思っちゃったりもするけど・・・

(技術に興味のない新卒エンジニアへの教育とか考えると、言語仕様のベースはシンプルで良い気もしちゃうので・・・)

ちなみに C# 7 っていうのは、VSでいうと「Visual Studio 2017」ってことになります。

まあ、それはさておき、今回フォーカスするのは「タプル」!

個人的には他言語(主に動的型言語勢)が先行して、.NETが後を追いかけた感のある機能ですが・・・これについてC# 7で進化するようなので、ちょっと探索してみたレポートです。

タプルとは?

タプルとは「複数のオブジェクトを1まとめとして扱うもの」です。
考え方によっては、「厳格な型定義」を軸とすると”邪道な方法論”と言われてもおかしくないけれど、柔軟な考え方をすれば「便利」というものかもしれないかなぁ、と。
あと匿名型とかDictionaryとか、似たような機能も存在するし使い分けとベストプラクティスが難しいけれど・・・

.NET 4で導入されたタプル(System.Tuple)

タプル自体は .NET 4 で「System.Tupleクラス」の導入により.NETにも取り入れられていました。
Tupleクラスの使い方は以下のような形です。

// 3つの値を持つタプルを定義
Tuple<int, string, string> emp = 
    new Tuple<int, string, string>(101, "ryuichi", "daigo");

// タプルを参照
Console.WriteLine(emp.Item1);
Console.WriteLine(emp.Item2);
Console.WriteLine(emp.Item3);

~実行出力結果~
101
ryuichi
daigo

んー、Item1、Item2、Item3と・・・まあ、そりゃそうだけど、「厳格な開発コーディング規約チーム(そのような組織がある会社であれば)」から、Tupleの使用は 承認を得にくいような仕様の機能ですよね・・・。

C# 7で導入されたタプル

で、C# 7.0におけるタプルの特徴は「名称(ラベル)に対して値を設定するタプル」を定義できることであると思います。
(これは、Swift言語なんかと同じテイストですね)
具体的な実装方法は以下の通り。

// タプルの定義
(string FirstName, string LastName, DateTime Birth) person = 
    (FirstName: "ryuichi", LastName: "daigo", Birth: new DateTime(1990, 1, 10));

// タプル値の参照
Console.WriteLine(
    string.Format("{0} {1} {2}", 
      person.FirstName, 
      person.LastName, 
      person.Birth.ToString("yyyy/MM/dd")));

(【型名】 名称, 【型名】 名称, 【型名】 名称・・・) といった形式でタプルオブジェクトを宣言し、値は (【名称】: 【値】, 【名称】: 【値】, 【名称】: 【値】, ・・・)と定義します。
利用する際は「person.FirstName」「person.LastName」「person.Birth」のように厳密な名称で参照します。仮にFirstName / LastName / Birthに該当する名称が間違っていた場合、コンパイルエラーが発生します。
また、もちろんインテリセンスも働きます。

使い方はこんな感じなのですが、私のような人間は
「これは言語仕様なのか?コンパイル後のILはどのようになっているのか?」
と気になってしまう訳で・・・
以下がコンパイル後のDLL(アセンブリ)を ILSPY で逆コンパイルしたコードになります。

// ビルド後のDLLをILSPYで逆コンパイルしたコード
ValueTuple<string, string, DateTime> person =
    new ValueTuple<string, string, DateTime>("ryuichi", "daigo", new DateTime(1990, 1, 10));
Console.WriteLine(
    string.Format("{0} {1} {2}", 
        person.Item1, 
        person.Item2, 
        person.Item3.ToString("yyyy/MM/dd")));

タプル変数定義は「ValueTuple」オブジェクトに変換されていました。
そして、各プロパティも Item1 / Item2 / Item3 と展開されています。
つまり、タプル内メンバーへの名称付きアクセスは C# 7 言語レベルでのサポート(お気遣い)であり、ILレベル / CLR レベルではタプルオブジェクトは ValueTuple型のオブジェクト、メンバーは Item 1 / Item2 / Item3・・・として扱われます。

また、ValueTupleオブジェクトについては C# 7 以前の現行のVS2015 & .NET4.6においても、Nugetで「System.ValueTuple」を参照することで利用可能です。

f:id:daigo-knowlbo:20161222010315p:plain

勿論この場合、Item1 / Item2 ・・・というようなメンバーアクセサを利用することになりますが。

まとめ

「まとめ」ってことではないですが・・・
えーと、タプルの使いどころのベストプラクティスは私自身堂々と記述できるほど確立していないというのが事実で、「シンプル」と「高機能」のバランスは難しいなあ・・・と思っております。
タプルに限らず、得られる結果への道筋・選択肢が複数ある事が最近は多く、バランスの良い規約や方向性を持つこと、技術的本質を見切ることが重要ではないかなぁ、と思っているのです。
という、モヤモヤした終わり方をするのです(笑)

では、またあ。

※2017/3/15追記
以下もご参考に
ryuichi111std.hatenablog.com

Xamarin + Prism 超入門(とりあえず動かしてみよう!)

[2017/8/3追記] Visual Studio 2017をターゲットとした焼き直し記事を書きました。

ryuichi111std.hatenablog.com

本エントリーはVisual Studio 2015をベースとしています。
Visual Studio 2017での作業手順は上記「<2017年8月版>Xamarin + Prism 超入門(とりあえず動かしてみよう!)」が最新となります。


今回はXamarinで本格的な(?)開発を行う際の「gettting started」的な記事を書きたいと思います。
要点としては「Xamarin + Prism」で開発する為の「はじめの一歩」の記事となります。
Macで Xamarin Studio を利用するケース」、「WindowsVisual Studio を利用するケース」毎に、誰にでも分かる様に画面キャプチャ付きで手順を説明したいと思います。

同様のブログがすでにいくつも書かれていると思いますが、自分自身の整理と、本記事がどなたかのお役に立てればと思います。

Prismって何?

Xamarinに限らず、ある程度しっかりしたシステムの開発を行う際には、レイヤー化アーキテクチャが導入されます。
Web開発だと「MVC (Model-View-Controller」や「MVP (Model-View-Presenter」などがありますね。
Wikipediaによると、MVCに関する最初の論文が発表されたのが1988年のことだそうです。そう、このようなソフトウェアアーキテクチャは、遥か昔から存在しています。

で、Xamarinにおけるメジャーなレイヤー化アーキテクチャは「MVVM (Model-View-ViewMode)」となります。
まあ、Xamarinというか、XAML関連技術においてMVVMが標準技術として利用されてきました。
Xamlの始まりはWPFですね。続いてSilverlightWindowsストアアプリ、UWP、とXaml技術は継続的進化を果たしています(私の様なベテランエンジニアに対しては、優しい、積み上げ型の知識が非常に役に立つ正常進化です)。
これらにMVVMアーキテクチャを適用するためのライブラリ(フレームワーク)がいくつか世の中には存在します。
代表的な(そしてXamarinに対応している)フレームワークとして以下のようなものがあります。

  • Prism
  • MVVM Light

今回紹介するのは Prism になります。
ネット上の情報を見る限り Prism がディファクトスタンダードとなりつつあるように感じます。
Xamarinの神様(Miguel de Icaza氏)が、Prism使おうよって仰ってますしね。

Prism公式サイトは以下です。

github.com

何故 Prism を使うのか?

それは Prism 公式サイトの以下の言葉に集約されています。

Prism is a framework for building loosely coupled, maintainable, and testable XAML applications in WPF, Windows 10 UWP, and Xamarin Forms.

ここでは、これ以上深く踏み込まず、次に進みます・・・

Xamarin Studio で Prism を使おう(Mac)

(1) Prism Template Pack のインストール

Xamarin Studioを起動します。
メニュー「Xamarin Studio Community→アドイン」を選択します。

f:id:daigo-knowlbo:20161211150641p:plain

以下の「アドインマネージャー」ウィンドウが表示されます。
「ギャラリー」タブを選択、右上の検索テキストボックスに「Prism」と入力、アドインリストから「Prism Template Pck」を選択し、右下の「インストール」ボタンをクリックします。

f:id:daigo-knowlbo:20161211150933p:plain

[2016/12/31 追記]
アドインマネージャーで検索して Prism Template Pack が表示されない場合は、Prism Template Packから直接アドインをダウンロードして、アドインマネージャーウィンドウ左下の「ファイルからのインストール」ボタンからインストールできます。

(2) Prismプロジェクトの作成

メニュー「ファイル→新しいソリューション」を選択します。

f:id:daigo-knowlbo:20161211151135p:plain

プロジェクトテンプレートとして「Xamarin.Forms→Prism Unity App」を選択を選択して、「次へ」ボタンをクリックします。

f:id:daigo-knowlbo:20161211151236p:plain

App Name等を適当に設定します。ここではApp Nameは PrismExample、Organization Identifierは jp.co.knowlbo としました。

f:id:daigo-knowlbo:20161211151401p:plain

続いてプロジェクトの構成を設定します。プロジェクト名・ソリューション名は共に PrismExample としました。

f:id:daigo-knowlbo:20161211151539p:plain

しばらく待っているとソリューション・プロジェクトが構成されます。
作成されたソリューションは以下の様になります。

f:id:daigo-knowlbo:20161211151741p:plain

通常のXamarin Formsと基本的には似た構成になります。

  • PCLとしてのXamarin Formsプロジェクトである「PrismExampleプロジェクト」。
  • Android固有実装用プロジェクトである「PrismExample.Droidプロジェクト」。
  • iOS固有実装用プロジェクトである「PrismExample.iOSプロジェクト」。

1つ大きく異なるのはPrismExampleプロジェクトに ViewModels / Views フォルダがあることです。
まあ、PrismはMVVMフレームワークですからこれがキモですね。

(3) ビルドエラーが出るから修正

自動生成されたソリューションは、いわゆる HelloWorld 的な実装が出力されています。
とりあえずビルドして実行してみよう!ということで、メニュー「ビルド→すべてリビルド」を選択します。

が・・・2016/12/11段階のPrism Template Packにおいては、ビルドエラーにその道を阻まれます(泣)
ビルドエラーは以下の通りです。

f:id:daigo-knowlbo:20161211152549p:plain

エラーメッセージは以下ですね。

CS0246: The type or namespace name `IPlatformInitializer' could not be found. Are you missing `Prism.Unity' using directive?    
CS0246: The type or namespace name `IUnityContainer' could not be found. Are you missing `Microsoft.Practices.Unity' using directive?  

「型か名前空間が見つからないんだけど・・・usingディレクティブ足りてないんじゃね?」って言ってますね。
なのでエラーが出ている「PrismExample.Droid¥MainActivity.cs」と「PrismExample.iOS¥AppDelegate.cs」に、以下の using を追加します。

using Prism.Unity;
using Microsoft.Practices.Unity;

ビルドは無事通るはずです。
※これはそもそも提供されているPrism Templata Packの問題かな?皆さんが利用する際には治っているかも・・・

(4) 実行してみる

メニュー「実行→デバッグなしで開始」を選択します。デフォルトだと iOS emulator での実行が構成されています。
実行結果は以下の通りです。

f:id:daigo-knowlbo:20161211153650p:plain

(5) せっかくなので、ちょっといじろうか・・・

ただ実行するだけでは、あまりにもつまらないので、以下の様な実装を付け加えようと思います。
以下の様な入力フォームとします。

f:id:daigo-knowlbo:20161211164101p:plain

名前を入れて・・・

f:id:daigo-knowlbo:20161211164354p:plain

決定ボタンを押すと、メッセージが表示されます。

f:id:daigo-knowlbo:20161211164551p:plain

実装コードは以下の通りです。

--- MainPage.xaml ---
<?xml version="1.0" encoding="utf-8"?>
<ContentPage 
    xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" 
    prism:ViewModelLocator.AutowireViewModel="True" 
    x:Class="PrismExample.Views.MainPage" 
    Title="MainPage">
  <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
    <Label Text="あなたの名前" />
    <Entry Text="{Binding YourName}" />
    <Button Text="決定" Command="{Binding DecisionCommand}" />
    <Label Text="{Binding Message}" />
  </StackLayout>
</ContentPage>
--- MainPageViewModel.cs ---
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;

using System;
using System.Collections.Generic;
using System.Linq;

namespace PrismExample.ViewModels
{
  public class MainPageViewModel : BindableBase, INavigationAware
  {
    // 名前入力Entry項目にバインドします
    private string _yourName;
    public string YourName
    {
      get { return _yourName; }
      set { SetProperty(ref _yourName, value); }
    }

    // メッセージ表示Label項目にバインドします
    private string _message;
    public string Message
    {
      get { return _message; }
      set { SetProperty(ref _message, value); }
    }

    // 決定ButtonのCommandにバインドします。
    private DelegateCommand _decisionCommand;
    public DelegateCommand DecisionCommand
    {
      get
      {
        return this._decisionCommand = this._decisionCommand ??
          new DelegateCommand(DecisionCommandExecute);
      }
    }

    public MainPageViewModel()
    {
    }

    private void DecisionCommandExecute()
    {
      this.Message = string.Format("{0}さん こんにちは", this.YourName);
    }

    public void OnNavigatedFrom(NavigationParameters parameters)
    {
    }

    public void OnNavigatedTo(NavigationParameters parameters)
    {
    }
  }
}

実装をずらずらと載せてしまいましたが、要点は以下になります。
(すみません、実装を載せておきながら、なのですが、中身の説明は本投稿の軸となる趣旨からはずれるので、要点のみの概略で、PrismやXamarin Formsの詳細技術は別投稿で取り上げさせていただきたいと思います。m( _ _ )m)

  • MainPage.xamlに 名前入力用 Entry 要素を追加
    EntryコントロールのTextプロパティの値、つまり入力された値は {Binding} 構文により MainPageViewModelクラス の YourNameプロパティ にデータバインディングします。

  • MainPage.xamlに 決定 Button 要素を追加
    Buttonコントロールの Commandプロパティ に、MainPageViewModelクラスの DecisionCommandプロパティ をデータバインドします。
    Button.Clickedイベントではなく、Commandにバインドする理由は「BindablePropertyが・・・・」などの技術的バックボーンがありますが、このお話はまた別のところで・・・

  • MainPage.xamlに メッセージ Label 要素を追加
    LabelコントロールのTextプロパティの値に、MainPageViewModeクラスの Messageプロパティ をデータバインディングします。
    MainPageViewModel側でMessageプロパティを変更すると、自動的にUIの表示も切り替わります。

  • MainPageViewModelにDelegateCommandプロパティを追加
    MainPageViewModelクラスに、DelegateCommand型のDecisionCommandプロパティを追加し、イベントハンドラとしてDecisionCommandExecute()メソッドを実装します。
    以下の様にMessageプロパティ・YourNameプロパティを扱うことでロジックを処理しています。xaml側とのデータバインディング処理によってViewModelクラス上でのロジック処理結果がUIに自動的に反映されます。

--- MainPageViewModel.csのコードスニペット ---

private void DecisionCommandExecute()
{
  this.Message = string.Format("{0}さん こんにちは", this.YourName);
}

こんな感じでView - ViewModelが疎結合に実装されました。

Visual Studio 2015 で Prism を使おう(Windows)

(1) Prism Template Pack のダウンロード & インストール

Visual Studio を起動し、メニュー「ツール→拡張機能と更新プログラム」を選択します。

f:id:daigo-knowlbo:20161211215627p:plain

以下の「拡張機能と更新プログラム」ウィンドウが表示されます。
左のツリーから「オンライン」を選択し、右上の検索テキストボックスに「Prism」と入力します。中央のリストに「Prism Template Pack」が表示されるので「ダウンロード」ボタンをクリックします。

f:id:daigo-knowlbo:20161211220650p:plain

ダウンロードが完了すると以下のインストールウィンドウが表示されるので「インストール」ボタンをクリックします。

f:id:daigo-knowlbo:20161211220915p:plain

インストールが完了すると、Visual Studioの再起動を求められますので、再起動を行います。

(2) Prismプロジェクトの作成

メニュー「ファイル→新規作成→プロジェクト」を選択します。

f:id:daigo-knowlbo:20161211221152p:plain

「新プロジェクト」ウィンドウが表示されるので、プロジェクトテンプレートとして「Prism→Prism Unity App(Xamarin.Forms)」を選択します。
プロジェクト名は「PrismExample」としました。

f:id:daigo-knowlbo:20161211221445p:plain

プロジェクトに含めるプラットフォーム選択ウィンドウが表示されます。今回は「ANDROID / iOS / UWP」を選択することにします。
MacのXamarin studioでは作成する事は出来なかった「UWP / STORE 8.1 / PHONE 8.1」が選択可能です。逆にiOSアプリはMacに接続しないとデバッグ・実行する事が出来ません。

f:id:daigo-knowlbo:20161211221715p:plain

プロジェクトの用意が完了するとMacへの接続情報設定ウィンドウが表示されますが、ここではそのまま設定せずに進みます。
次にUWPのターゲットバージョン選択ウィンドウが表示されます。ここでは、デフォルトのままOKをクリックする事とします。

f:id:daigo-knowlbo:20161211222233p:plain

以上でプロジェクトの生成が完了しました。

(3) ビルド

メニュー「ビルド→ソリューションのリビルド」を選択します。

f:id:daigo-knowlbo:20161211223006p:plain

しばらく時間がかかると思いますが、ビルドは正常終了するはずです。

(4) 実行

せっかくWindows環境なので、UWPアプリケーションとして実行してみましょう。
ソリューションエクスプローラ上で、「PrismExample.UWP」をマウス右クリックし、「スタートアッププロジェクトに設定」を選択します。

f:id:daigo-knowlbo:20161211230729p:plain

実行する前にアプリケーションを配置する必要があります。
メニュー「ビルド→PrismExample.UWPの配置」を選択します。

f:id:daigo-knowlbo:20161211223549p:plain

対象のPCが開発者モードになっていなかった場合、以下のウィンドウが表示されます。

f:id:daigo-knowlbo:20161211224020p:plain

UWPとして実行する場合、Windows上での実行になりますので、Windows設定を開発者モードにする必要があります。
「開発者モード」を選択しましょう。

f:id:daigo-knowlbo:20161211224205p:plain

改めて、メニュー「ビルド→PrismExample.UWPの配置」を選択します。
配置が完了したら、ツールバーから「▶︎ローカルコンピューター」をクリックしましょう。

f:id:daigo-knowlbo:20161211224345p:plain

デバッグモードで PrismExample.UWP が実行されました。

f:id:daigo-knowlbo:20161211224551p:plain

まとめ

Xamarinに限らずアーキテクチャを考慮せず、ただただコードビハインドにロジックを書き連ねるような実装方法が行われる事があります。
レイヤー化アーキテクチャに限らず、testableである事、maintainableである事、適切な責務の分離が行われている事などは非常に重要です。
XamarinにおけるPrismの導入に限らず、システムソリューションを構築する際は、一度立ち止まってベストプラクティスなアーキテクチャを検討したいものです。

今回は Xamarin + Prism の入り口の説明でしたので、今後はより技術的に踏み込んだトピックも取り上げていければと思います。

Azure DocumentDB Emulatorを使ってみた。で、.NET Coreから操作した話。

技術者としての尊敬の対象である Scott Hanselman 氏のブログで「Azure DocumentDB Emulator」についての記事が書かれていたので、自分でも使ってみました。

www.hanselman.com

こんなことをやった

「Azure DocumentDB」は、もはや、広く知られた Azureが提供する NoSQL データベースです。
いわゆる「ちょー早くて、ちょースケーラブルで、RDBの概念は捨ててから使ってね」っていうデータベースですね。
NoSQLは、その登場以来、どこにどう使うべきか?という部分についての議論・検討が行われ、まだまだ一般化していないように思います。
私自身も実は会社の業務としてNoSQLを採用したプロジェクトに関わった事はありません・・・

まあ、それはさておき(また、本投稿はNoSQLの本質を議論する趣旨ではないので・・・)、今回は以下のことを試してみたので、それらをまとめておこうと思います。

  • 「Azure DocumentDB Emulator」をインストールしてみた
  • 「.NET Core Consoleアプリ」から「Azure DocumentDB Emulator」に接続してデータ操作してみた
  • 「Azure DocumentDB(クラウド)」から「Azure DocumentDB Emulator(ローカル)」にデータをエクスポート(リストア)してみた
  • データのコンソール出力にはSerilogを使った

「Azure DocumentDB Emulator」のインストール

さあ、今回の主役である「Azure DocumentDB Emulator」をインストールします。
この「Azure DocumentDB Emulator」は、2016/11にリリースされました。
これは クラウドのAzure DocumentDB をローカルPC上でエミュレートするソフトウェアです。オフライン状態でも、Azure DocumentDB開発が可能になります。
以下のリンクページ 上部の「Download the Emulator」をクリックするとインストーラをダウンロードする事が出来ます。
(ちなみにこれはWindows版のみでMac版は残念ながらありません)

docs.microsoft.com

ダウンロードした msi ファイルは、ダブルクリックし「Next」や「OK」や、指示通りにクリックしていればインストール完了します。
インストールが完了すると、自動的に起動し、タスクトレイに以下のようなアイコンが表示されます(スタートメニューから「DocumentDB Emulator」を選択しても明示的に起動する事が出来ます)。

f:id:daigo-knowlbo:20161208015000p:plain

また、このアイコンをマウス右クリックすると以下のようなメニューが表示されます。

f:id:daigo-knowlbo:20161208015205p:plain

「Open Data Explorer...」をクリックすると以下のような管理画面が表示されます(インストール直後は、この画面も自動で表示されるはずです)。

f:id:daigo-knowlbo:20161208015308p:plain

見てわかるように「https://localhost:8081/_explorer/index.html#」という8081ポートでホストされた管理コンソール画面となります。
「.NET / .NET Core / Java...」等のタブが有り、ここに表示されている「Code Samples」をクリックすると、何も考えなくても「ビルド→実行」が可能なサンプルプロジェクトをダウンロード可能です。
画面上部の「Explorer」タブをクリックすると、DocumentDB Emulatorに保存されているデータベース内容を確認する事が出来ます(以下の画面)。
初期状態では 空 になります。

f:id:daigo-knowlbo:20161208015816p:plain

「.NET Core Consoleアプリ」+「Azure DocumentDB Emulator」でデータ保存・読み込み

では、せっかくなので最新の Visual Studio 2017 RC + .NET Core を使って DocumentDB Emulator に接続してみます。

①プロジェクトの作成

Visual Studio 2017 RCを起動し、メニュー「ファイル→新規作成→プロジェクト」を選択します。

f:id:daigo-knowlbo:20161208020502p:plain

ここではシンプルに、テンプレートとして「Console App(.NET Core)」を選択し、プロジェクト名は「DocDbExampleCoreConsole」としました。

②Nuget参照の追加

次にNugetで必要なライブラリへの参照を追加します。
メニュー「プロジェクト→Nuget パッケージの管理」を選択します。
まず必要なのは「Microsoft.Azure.DocumentDB.Core」になります。

f:id:daigo-knowlbo:20161208021044p:plain

さらに、これは本投稿の本質ではありませんが、コンソールへのデータログ出力用に「Serilog」および、そのコンソールSinkである「Serilog.Sinks.Console」も追加します。

f:id:daigo-knowlbo:20161208021059p:plain

f:id:daigo-knowlbo:20161208020914p:plain

③モデルクラスの作成

DocumentDBに保存するモデルクラスを作成します。
ブログの投稿内容を想定したモデルを作成することとします。
メニュー「プロジェクト→クラスの追加」を選択し、表示された「新しい項目の追加」ウィンドウで「コード→クラス」を選択し、名前は「BlogPost.cs」とします。

f:id:daigo-knowlbo:20161208022754p:plain

実装は以下のとおりであり、ブログ投稿を表す「BlogPostクラス」、ブログ投稿者を表す「Userクラス」、ブログ投稿へのコメントを表す「Commentクラス」から構成されます。

// BlogPost.cs
using System.Collections.Generic;
using Newtonsoft.Json;

namespace DocDbExampleCoreConsole
{
  /// <summary>
  /// ブログポスト(1投稿)を表すクラスです。
  /// </summary>
  public class BlogPost
  {
    [JsonProperty(PropertyName = "id")]
    public string Id { get; set; }

    [JsonProperty(PropertyName = "title")]
    public string Title { get; set; }

    [JsonProperty(PropertyName = "contents")]
    public string Contents { get; set; }

    [JsonProperty(PropertyName = "author")]
    public User Author { get; set; }

    [JsonProperty(PropertyName = "comments")]
    public List<Comment> Comments { get; set; }
  }

  /// <summary>
  /// ブログ投稿者を表すクラスです。
  /// </summary>
  public class User
  {
    [JsonProperty(PropertyName = "id")]
    public string Id { get; set; }

    [JsonProperty(PropertyName = "name")]
    public string Name { get; set; }
  }

  /// <summary>
  /// ブログ投稿へのコメントを表すクラスです。
  /// </summary>
  public class Comment
  {
    [JsonProperty(PropertyName = "id")]
    public string Id { get; set; }

    [JsonProperty(PropertyName = "text")]
    public string Text { get; set; }
  }
}

リポジトリクラスの作成

モデルクラスをDocumentDBに出し入れするためのデータベースアクセス用のリポジトリクラスを作成します。
メニュー「プロジェクト→クラスの追加」を選択し、表示された「新しい項目の追加」ウィンドウで「コード→クラス」を選択し、名前は「BlogPostRepository.cs」とします。

f:id:daigo-knowlbo:20161208023254p:plain

実装は以下の通りです。

// BlogPostRepository.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;

namespace DocDbExampleCoreConsole
{
  public class BlogPostRepository
  {
    /// <summary>
    /// 任意のデータベースID
    /// </summary>
    private static readonly string DatabaseId = "Blog";

    /// <summary>
    /// 任意のコレクションID
    /// </summary>
    private static readonly string CollectionId = "BlogPost";

    /// <summary>
    /// エンドポイント
    /// </summary>
    private static readonly string EndPoint = "https://localhost:8081/";

    /// <summary>
    /// 認証キー(固定)
    /// </summary>
    private static readonly string AuthKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";

    private static DocumentClient client;

    /// <summary>
    /// ブログポストデータを作成します。
    /// </summary>
    /// <param name="blogPost"></param>
    /// <returns></returns>
    public static async Task<Document> CreateBlobPostAsync(BlogPost blogPost)
    {
      return await client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId), blogPost);
    }

    /// <summary>
    /// すべてのブログポストデータを取得します。
    /// </summary>
    /// <returns></returns>
    public static async Task<IEnumerable<BlogPost>> GetAllBlogPostsAsync()
    {
      IDocumentQuery<BlogPost> query = client.CreateDocumentQuery<BlogPost>(
        UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
        new FeedOptions { MaxItemCount = -1 })
        .AsDocumentQuery();

      List<BlogPost> results = new List<BlogPost>();
      while (query.HasMoreResults)
      {
        results.AddRange(await query.ExecuteNextAsync<BlogPost>());
      }

      return results;
    }

    /// <summary>
    /// データベース・コレクションの初期化を行います。
    /// </summary>
    public static void Initialize()
    {
      client = new DocumentClient(new Uri(EndPoint), AuthKey, new ConnectionPolicy { EnableEndpointDiscovery = false });
      CreateDatabaseIfNotExistsAsync().Wait();
      CreateCollectionIfNotExistsAsync().Wait();
    }
    
    /// <summary>
    /// 存在しなければデータベースを作成します。
    /// </summary>
    /// <returns></returns>
    private static async Task CreateDatabaseIfNotExistsAsync()
    {
      try
      {
        await client.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(DatabaseId));
      }
      catch (DocumentClientException e)
      {
        if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
        {
          await client.CreateDatabaseAsync(new Database { Id = DatabaseId });
        }
        else
        {
          throw;
        }
      }
    }

    /// <summary>
    /// 存在しなければコレクションを作成します。
    /// </summary>
    /// <returns></returns>
    private static async Task CreateCollectionIfNotExistsAsync()
    {
      try
      {
        await client.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId));
      }
      catch (DocumentClientException e)
      {
        if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
        {
          await client.CreateDocumentCollectionAsync(
            UriFactory.CreateDatabaseUri(DatabaseId),
            new DocumentCollection { Id = CollectionId },
            new RequestOptions { OfferThroughput = 1000 });
        }
        else
        {
          throw;
        }
      }
    }
  }
}

リポジトリクラス実装のポイントは以下の通りです。

  • DatabaseId / CollectionId
    これらは任意の名称をつけます。ここでは「Blog」「BlogPost」としました。

  • EndPoint
    エンドポイントはデフォルトでは「https://localhost:8081/」となります。

  • AuthKey
    Azure DocumentDB Emulator では認証キーは固定で「C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==」と決められています。

  • Microsoft.Azure.Documents.Client.DocumentClient
    DocumentDBへの接続クライアント(繋ぎ役)は「DocumentClientクラス」となります。本サンプルでは Initialize() メソッド内で初期化を行っています。

  • Initialize() / CreateDatabaseIfNotExistsAsync() / CreateCollectionIfNotExistsAsync()
    データベースは、データ操作を行う前に初期作成を行う必要があります(RDBでいうところのCREATE DATABASE / CREATE TABLE)。これに該当する処理をCreateDatabaseIfNotExistsAsync() / CreateCollectionIfNotExistsAsync()で実装しています。両メソッドは Initialize() から呼び出されています。つまり、初期化処理として BlogPostRepository.Initialize() を呼び出す必要があります。

  • CreateBlobPostAsync(BlogPost blogPost)
    ブログポスト(ドキュメント)をデータベース コレクション」に追加します(RDBのINSERT)。

  • GetAllBlogPostsAsync()
    登録されたすべてのブログポスト(ドキュメント)を取得しています(RDBのSELECT)。

⑤main()の実装(リポジトリクラスの呼び出し)

用意したリポジトリクラス(BlogPostRepository)を利用して、BlogPostモデルクラスのデータベースへの出し入れ処理を実装します。
以下がコンソールアプリケーションのmain処理の実装になります。

// Program.cs
using System.Threading.Tasks;
using System.Collections.Generic;

using Serilog;

namespace DocDbExampleCoreConsole
{
  class Program
  {

    private IEnumerable<BlogPost> blogPosts = null;

    static void Main(string[] args)
    {
      Program program = new Program();

      // データベースを初期化
      program.InitializeDatabase();

      // データを挿入
      program.InitializeData().Wait();

      // データを抽出
      program.GetAllBlogPosts().Wait();

      // 抽出したデータをコンソール出力
      Log.Logger = new LoggerConfiguration()
        .WriteTo.Console()
        .CreateLogger();
      foreach (var blogPost in program.blogPosts)
      {
        Log.Information("{@BlogPost}", blogPost);
      }

      System.Console.ReadLine();
    }

    /// <summary>
    /// データベース・コレクションを初期化します。
    /// </summary>
    private void InitializeDatabase()
    {
      // データベース・コレクションを作成
      BlogPostRepository.Initialize();
    }

    /// <summary>
    /// BlogPostサンプルデータを作成します。
    /// </summary>
    /// <returns></returns>
    private async Task InitializeData()
    {
      // 50件、テスト投稿を作成
      for (int i = 0; i < 50; i++)
      {
        BlogPost blogPost = new BlogPost() {
          Title = string.Format("Title {0}", i),
          Contents = string.Format("Hi! This contents no is '{0}'.", i),
          Author = new User() { Name = "nanasi san" },
          };
        blogPost.Comments = new List<Comment>();
        blogPost.Comments.Add(new Comment() { Text = "Good job!!" });

        await BlogPostRepository.CreateBlobPostAsync(blogPost);
      }
    }

    /// <summary>
    /// すべてのBlogPostデータを取得します。
    /// </summary>
    /// <returns></returns>
    private async Task GetAllBlogPosts()
    {
      this.blogPosts = await BlogPostRepository.GetAllBlogPostsAsync();
    }
  }
}

mainクラス実装のポイントは以下の通りです。

  • program.InitializeDatabase();
    リポジトリークラスの呼び出しにより、データベース・コレクションを作成しています。

  • program.InitializeData().Wait();
    リポジトリークラスの呼び出しにより、BlogPostサンプルデータを50件作成しています。

  • program.GetAllBlogPosts().Wait();
    リポジトリークラスの呼び出しにより、データベースコレクションに登録された全てのBlogPostデータ抽出しています。

  • ログ出力
    抽出の結果として得られたデータは「IEnumerable<BlogPost>」型です。
    個別データである BlogPost オブジェクトをSerilogでコンソール出力しています。
    Serilogを使用する理由は、オブジェクトのログ出力に際し、子プロパティを含めた形で自動でJSON形式出力してくれる機能を活用する為です。

以下がDocDbExampleCoreConsoleコンソールアプリケーションの実行結果となります。

2016-12-08 02:37:27 [Information] BlogPost { Id: "43eb6293-c532-46dd-969d-e6e4258bcd7d", Title: "Title 0", Contents: "Hi・・This contents no is '0'.", Author: User { Id: null, Name: "nanasi san" }, Comments: [Comment { Id: null, Text: "Good job!!" }] }
2016-12-08 02:37:27 [Information] BlogPost { Id: "86380c10-c45e-46e5-9320-a432be8e838f", Title: "Title 1", Contents: "Hi・・This contents no is '1'.", Author: User { Id: null, Name: "nanasi san" }, Comments: [Comment { Id: null, Text: "Good job!!" }] }
2016-12-08 02:37:27 [Information] BlogPost { Id: "77350d85-6e11-49ac-9f52-d14729cb41b4", Title: "Title 2", Contents: "Hi・・This contents no is '2'.", Author: User { Id: null, Name: "nanasi san" }, Comments: [Comment { Id: null, Text: "Good job!!" }] }
2016-12-08 02:37:27 [Information] BlogPost { Id: "e151fef2-74d8-4735-a89a-971603169c36", Title: "Title 3", Contents: "Hi・・This contents no is '3'.", Author: User { Id: null, Name: "nanasi san" }, Comments: [Comment { Id: null, Text: "Good job!!" }] }
2016-12-08 02:37:27 [Information] BlogPost { Id: "580291de-c99a-4893-b416-48954847413a", Title: "Title 4", Contents: "Hi・・This contents no is '4'.", Author: User { Id: null, Name: "nanasi san" }, Comments: [Comment { Id: null, Text: "Good job!!" }] }
2016-12-08 02:37:27 [Information] BlogPost { Id: "a80815e6-5d03-49e9-b12e-a2892f504dbd", Title: "Title 5", Contents: "Hi・・This contents no is '5'.", Author: User { Id: null, Name: "nanasi san" }, Comments: [Comment { Id: null, Text: "Good job!!" }] }
...以下省略

⑥「Azure DocumentDB Emulator」のExplorerからデータを確認

「Azure DocumentDB Emulator」の管理画面である「https://localhost:8081/_explorer/index.html#」から投入されたBlogPostデータを参照してみます。
以下の画面のようにデータを確認する事が出来ます。

f:id:daigo-knowlbo:20161208030020p:plain

ちなみにタスクトレイアイコン マウス右ボタン「Reset Data...」をクリックすると、簡単にきれいさっぱりデータが消去されるのでお気を付けください。

f:id:daigo-knowlbo:20161208030235p:plain

「Azure DocumentDB」から「Azure DocumentDB Emulator」にデータを(エクスポート)リストア

最後にクラウド上の Azure DocumentDB から Azure DocumentDB Emulator にデータリストアする手順を説明しておきます。

「本来の実装はクラウドのAzure DocumentDBで行っているが、出先等で一時的にローカルエミュレータ開発をしたい」
「今までEmulatorが無かったから、常にクラウドに接続していたけれど、クラウドエミュレータを平行利用開発したい」

等々の場合に有効かと・・・

以下のページから「Azure DocumentDB Data Migration Tool」をダウンロードします。

Download Azure DocumentDB Data Migration Tool from Official Microsoft Download Center

ダウンロードしたzipファイルを任意のフォルダに解凍し dtui.exe を起動します。

f:id:daigo-knowlbo:20161208031143p:plain

「Nextボタン」をクリックします。

表示された以下のウィンドウにて、Import from は「DocumentDB」、ConnectionString は「Azure上のDocumentDBへの接続文字列(Database=xxx も忘れずに)」、Collection は「インポート対象のコレクション名」を入力して「Nextボタン」をクリックします。

f:id:daigo-knowlbo:20161208031904p:plain

以下の画面において、export先(つまり、ここではローカルの Azure DocumentDB Emulator)の設定を行います。
ConnectionString は、EndPoint=「https://localhost:8081」・AccountLey=「固定値」Database=「任意(ここではBlog)」となります。
入力したら「Nextボタン」をクリックします。

f:id:daigo-knowlbo:20161208032231p:plain

以下のウィンドウが表示されたら「Next」ボタンをクリックします(必要であれば「エラーログファイル」の設定も行って)。

f:id:daigo-knowlbo:20161208035350p:plain

設定内容の最終確認を行う以下の画面が表示されいます。
問題なければ「Next」ボタンをクリックします。

f:id:daigo-knowlbo:20161208032714p:plain

以上で「クラウド Azure DocumentDB」→「ローカル Azure DocumentDB Emulator」へのデータのリストアが完了しました。

f:id:daigo-knowlbo:20161208032642p:plain

まとめ

NoSQLが登場したのはいつだったでしょうか・・・RedisやCassandra、memcashedに興奮し、迷走し・・・Azure黎明期にも当時のNoSQL(DoucmentDBという名称ではなかったと思う・・・)に興奮した記憶があります。
ビジネスアプリケーションにおいては、まだまだNoSQLの導入は遅れているように思います。同時にNoSQLがRDBにとって代わる技術では無い事も事実です。
RDBの堅牢なデータ管理とNoSQLのパフォーマンスを適材適所に組み合わせたシステムの開発、という提案を開発者として行っていきたいものです。

で、まあ新しい技術への探求は相変わらず楽しいのですよね^^

Visual Studio for Mac + Visual Studio Team Servicesでソース管理する

仕事レベルのプログラム開発の場合、ソース管理は必ず必要となります。
しかし、今時は個人開発であってもソース管理は必要ですし、また、サンプルの実装をGithubで公開するといった事も日常的な事でしょう。

今回は開発環境として「Visual Studio for Mac」を、ソース管理に「Visual Studio Team Services(以下VSTS)」を使用する手順について説明します(ソース管理はGitです)。

VSTSにプロジェクトを構成する

[Step 1] プロジェクトを作成

ブラウザで自分のVSTSサイトにアクセスします。
ダッシュボード上の「Recent projects & teams」下の「New」をクリックします。

f:id:daigo-knowlbo:20161204153616p:plain

「Create team project」ウィンドウが表示されますので、必要項目を入力し「Create project」ボタンをクリックします。Version ControlはGitにします。

f:id:daigo-knowlbo:20161204153751p:plain

しばらく待つとプロエジェクトの作成が完了します。「Navigate to project」ボタンをクリックしてプロジェクトポータルに移動しましょう。

f:id:daigo-knowlbo:20161204154136p:plain

[Step 2] セキュリティ設定を実施

右上のユーザーアイコンをクリックし、表示されたポップアプメニューから「Security」をクリックします。

f:id:daigo-knowlbo:20161204154228p:plain

Personal access tokenの「Add」ボタンをクリックします。(Personal access tokenの詳細は後述します)

f:id:daigo-knowlbo:20161204154406p:plain

Descriptionを任意で入力します。アクセストークンの有効期限や認可範囲の細かな設定が可能ですが、ここではひとまずデフォルト値の90日間の有効期限、フルスコープ権限とします。画面下部の「Create token」ボタンをクリックします。

f:id:daigo-knowlbo:20161204154756p:plain

f:id:daigo-knowlbo:20161204154819p:plain

トークンが発行されますので、これをコピーして手元に保存しておきます。

f:id:daigo-knowlbo:20161204155124p:plain

[Step 3] GitのURLを取得(確認)

左上のプロジェクト選択UIから今回作成した「VsMacAndVsts」を選択します。

f:id:daigo-knowlbo:20161204155310p:plain

「Code」タブを選択します。

f:id:daigo-knowlbo:20161204155506p:plain

GitのURLをコピーします。

f:id:daigo-knowlbo:20161204155557p:plain

以上で VSTS 側の設定準備は整いました。

Personal access tokenとは

「Personal access token」は Visual Studio for Mac からVSTSへGitアクセスするために必要となるトークンです。
以下のMicrosoft公式サイトに記述があるように「Microsoftアカウント・Azure AD認証によるネイティブ認証に対応していないクライアントからの資格情報認証の為のトークン」となります。

www.visualstudio.com

具体的には、パーソナルアクセストークンはVSTSにアクセスする際の「ユーザー名に対応するパスワード」となります。

Visual Studio for Macプロジェクトの作成

では、作成したVSTSでソース管理を行うプロジェクトをVisual Studio for Macで作成します。

[Step 4] VS for Macプロジェクトを作成

Visual Studio for Macを起動して「New project...」ボタンをクリックします。

f:id:daigo-knowlbo:20161204161054p:plain

ここでは .NET Core Console アプリケーションを作成することとします。

f:id:daigo-knowlbo:20161204161155p:plain

プロジェクト名や保存場所を設定します。ここではプロジェクト名は「HelloVstsConsole」としました。
また、「バージョン コントロールに Git を使用します。」にチェックを付けます。

f:id:daigo-knowlbo:20161204161244p:plain

[Step 5] プロジェクトに対してリモートリポジトリ(VSTS)を設定

プロジェクトが作成できたら、メニュー「バージョン管理→ブランチとリモートを管理する」を選択します。

f:id:daigo-knowlbo:20161204161511p:plain

「Git リポジトリの構成」ウィンドウが表示されるので、「リモート ソース」タブを選択し「追加」ボタンをクリックします。

f:id:daigo-knowlbo:20161204161719p:plain

「リモート ソース」ウィンドウが表示されたら、任意の名前を設定し、「URL」項目にGITリポジトリのURLを設定します。
このURLは上述「VSTSにプロジェクトを構成する」の「Step 3」で取得(確認)したURLになります。

f:id:daigo-knowlbo:20161204161907p:plain

ここまでで、Visual Studio for Macにおけるプロジェクトに対する下準備が完了しました。

[Step 6] ローカルリポジトリへコミット

では、作成したHelloVstsConsoleプロジェクトのGitソース管理へのコミット作業を行いましょう。
メニュー「バージョン管理→ソリューションを確認してコミット」を選択します。

f:id:daigo-knowlbo:20161204163047p:plain

追加されたソースの一覧を表示されますので、「コミット」ボタンをクリックします。

f:id:daigo-knowlbo:20161204163217p:plain

「ファイルをコミットする」ウィンドウが表示されます。
「コミットメッセージ」を入力し、「コミット」ボタンをクリックします。
この操作により、「ローカルリポジトリ」に対するコミットが完了します。(つまり、まだVSTSのソースは更新されていません。)

f:id:daigo-knowlbo:20161204164658p:plain

[Step 7] VSTSへプッシュ

次にVSTSにソースをアップしましょう。
メニュー「バージョン管理→変更をプッシュ」を選択します。

f:id:daigo-knowlbo:20161204164848p:plain

リポジトリへプッシュ」ウィンドウが表示されます。
「プッシュ先」は先程設定した「VsMacAndVsts」を選択し、「変更をプッシュ」ボタンをクリックします。

f:id:daigo-knowlbo:20161204165020p:plain

「Git 資格情報」ウィンドウが表示されます。
「ユーザー名」には、VSTSへのログインIDを設定します。
「パスワード」には、「VSTSにプロジェクトを構成する」のStep 2で得た「パーソナルアクセストークン」を設定します。

f:id:daigo-knowlbo:20161204165212p:plain

以上で「ローカルリポジトリへのコミット」→「VSTSへのプッシュ」が完了しました。

プッシュされたソースを確認する

では、再度VSTSサイトにアクセスし、Visual Studio for Macで作成したプロジェクトコードがアップされたか確認してみましょう。
以下が VsMacAndVsts プロジェクトのCodeの表示です。Visual Studio for Macで作成したプロジェクトを確認することができます。

f:id:daigo-knowlbo:20161204165622p:plain

ソースを変更して再コミット&プッシュ

ではProgram.csを以下のように修正してみます。
(元々は「Console.WriteLine("Hello World!");」でした。)

using System;

class Program
{
  static void Main(string[] args)
  {
    Console.WriteLine("変更したよ");
  }
}

再びローカルリポジトリにコミットします。(メニュー「バージョン管理→ソリューションを確認してコミット」→コミットボタン)

f:id:daigo-knowlbo:20161204170047p:plain

続いてVSTSリモートリポジトリに変更をプッシュします。(メニュー「バージョン管理→変更をプッシュ」)

f:id:daigo-knowlbo:20161204170126p:plain

VSTSサイトを確認すると変更が正しくプッシュされたことを確認することができます。

f:id:daigo-knowlbo:20161204170138p:plain

まとめ

はい。という事で Visual Studio for Mac + Visual Studio Team Services によるソース管理(バージョン管理)の設定手順の説明となりました。
VSS / SVN / TFS / GIT・・・と多種多様なソース管理(バージョン管理)システム、そして、Eclipse / Visual Studio / Visual Studio for Mac / Visual Studio Code / Web Storm 等々の多くの開発環境が乱立しています。
各種技術を学び、適切なツール・適切なソリューションを選択して、幸せな開発生活を送りたいですね。

Entity Framework Core 1.1のHasField()とUsePropertyAccessMode()を使ってみた

Entity Framework Core 1.0 → 1.1における機能追加の1つとして「HasField()メソッドの追加」というものがあります。
Entity Frameworkでは、基本的に
「モデルクラス=データベース上のテーブル」
「モデルクラスのプロパティ=データベーステーブル上のカラム」
というマッピングを行います。
HasField()を利用すると「(クラス)フィールド」をデータベーステーブルカラムにマップすることが出来ます。

テスト環境

本投稿のテスト環境は以下の通りです。

C:\Users\ryuichi>dotnet --info
.NET Command Line Tools (1.0.0-preview4-004110)

Product Information:
 Version:            1.0.0-preview4-004110
 Commit SHA-1 hash:  740a7fe3fd

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.14393
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\1.0.0-preview4-004110

.NET Core Downloadsからダウンロードできるstableなバージョンではなく、デイリービルドされているGithub上のhttps://github.com/dotnet/cliから2016/11/23にダウンロードを行いました。
このバージョンは dotnet new で .csproj を生成します。

データベーステーブル定義

まず、今回使用するデータベーステーブルの定義は以下のようなものを想定します。

f:id:daigo-knowlbo:20161124025924p:plain

テーブル名は UserAccount で、列定義は「主キーである ID、氏名を表す FirstName / LastName、TwitterアカウントIDを表す ValidatedTwitterId」という構成です。ValidatedTwitterIdとは、確かにTwitter上に対象IDが存在すると検証したTwitterIDという意味です。

プロジェクトを作成

コマンドプロンプトを開き、dotnetコマンドによりプロジェクトを作成します。
ここでは、c:\Projects\dotNet\efCore11Exampleフォルダで以下のコマンドを実行しました。

dotnet new

.csprojに参照設定を追加

本サンプルではEntity Framework 1.1 / HttpClient を利用します。
csprojファイルは以下のように PackegeReference を追加しています。

// efCore11Example.csproj
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
  
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp1.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="**\*.cs" />
    <EmbeddedResource Include="**\*.resx" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NETCore.App">
      <Version>1.0.1</Version>
    </PackageReference>
    <PackageReference Include="Microsoft.NET.Sdk">
      <Version>1.0.0-alpha-20161104-2</Version>
      <PrivateAssets>All</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore">
      <Version>1.1.0</Version>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer">
      <Version>1.1.0</Version>
    </PackageReference>
    <PackageReference Include="System.Net.Http">
      <Version>4.3.0</Version>
    </PackageReference>
  </ItemGroup>
  
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

.csprojを修正した後、「dotnet restore」を実行しておきましょう。

モデルクラスの実装

UserAccountモデルクラスを実装します。

// UserAccount.cs
using System.Net.Http;
using System.Threading.Tasks;

namespace efCore11Example.Models 
{
  public class UserAccount
  {
    public int Id { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    private string _validatedTwitterId;

    public async Task SetTwitterId(string twitterID)
    {
      using (var client = new HttpClient())
      {
        string url = "https://twitter.com/" + twitterID;
        var response = client.GetAsync(url).Result;
        var stringResponse = await response.Content.ReadAsStringAsync();
        if( !stringResponse.Contains("<form class=\"search-404\" action=\"https://twitter.com/search\" method=\"get\">") )
        {
          this._validatedTwitterId = twitterID;
        }
      }
      
    }
    public string GetTwitterId()
    {
      return this._validatedTwitterId;
    }
  }
}

UserAccountテーブルのカラムに該当する「Id / FirstName / LastName」は、通常通りのプロパティとして実装します。
ValidatedTwitterId列に対応するプロパティは定義せず、代わりに「validatedTwitterId」フィールドを用意します。
また、
validatedTwitterIdフィールド値を設定するための「SetTwitterId()メソッド」を用意します。このメソッドは、twitterサイトにHTTPリクエストを行い、TwitterIDの存在チェックを行っています。あまり良い実装ではありませんが、Twitterに対して存在しないTiwtterIDのページをリクエストした際、
”レスポンスされたHTMLに「class="search-404"」というタグが含まれる”
という事柄を利用してTwitterIDの存在を確認しています。

DbContextクラスの実装

次に、DbContextクラスを継承したExampleDbContextクラスを実装します。

// ExampleDbContext.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;

namespace efCore11Example.Models 
{
  public class ExampleDbContext : DbContext
  {
    public DbSet<UserAccount> UserAccounts { get; set; }

    public ExampleDbContext()
    {
    }
    
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
      optionsBuilder.UseSqlServer(@"Server=tcp:rdexampledb2svr.database.windows.net,1433;Initial Catalog=RdExampleDb2;Persist Security Info=False;User ID=[user id];Password=[password];MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;");
    }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
      modelBuilder.Entity<UserAccount>()
        .Property<string>("ValidatedTwitterId")
        .HasField("_validatedTwitterId")
        .UsePropertyAccessMode(PropertyAccessMode.Field);
    }
  }
}

DbSetとしてUserAccountsプロパティを定義します。
OnConfiguring()メソッドで接続先SQL Serverへの接続文字列を設定しました。ここでは私のAzure上のSQL Databaseへの接続文字列の設定を行っています。

そして、OnModelCreating()での実装が今回の肝になります。
UserAccountモデルクラスとデータベーステーブルのマッピングについて追加の情報を設定しています。
「.Property("ValidatedTwitterId")」は、ValidatedTwitterIdという名称のプロパティをモデルクラスに追加することを意味しています。つまり、マップ先のデータベーステーブルのカラム名になります。
「.HasField("validatedTwitterId").UsePropertyAccessMode(PropertyAccessMode.Field);」は、ValidatedTwitterIdプロパティに該当するモデルクラスの要素は「validatedTwitterId」であり、それは「フィールドとしてアクセス可能である」と定義しています。

以上で、モデルクラスおよびDbContextの準備が整いました。

ExampleDbContext / UserAccountを利用してDB操作を行う

main()メソッドに、ExampleDbContextを利用してUserAccountの追加 および 読み取り を行う実装を行うこととします。

// Program.cs
using System;

using efCore11Example.Models;

namespace efCore11Example
{
  class Program
  {
    static void Main(string[] args)
    {
       // レコードを追加
       AddAccountUsers();
       // レコードを読み取り
       ReadAccountUsers();
    }

    // 2件のUserAccountをDBに追加します。
    // 1件は不正なTwitterIDです。
    public static void AddAccountUsers()
    {
      using(ExampleDbContext context = new ExampleDbContext()) 
      {
        UserAccount userAccount = new UserAccount();
        userAccount.Id = 1;
        userAccount.FirstName = "ryuichi";
        userAccount.LastName = "daigo";
        userAccount.SetTwitterId("ryuichi111std").Wait();

        context.UserAccounts.Add(userAccount);

        UserAccount userAccount2 = new UserAccount();
        userAccount2.Id = 2;
        userAccount2.FirstName = "fusei";
        userAccount2.LastName = "account";
        userAccount2.SetTwitterId("fuseinatwitterid").Wait();

        context.UserAccounts.Add(userAccount2);

        context.SaveChanges();
      }
    }

    // UserAccountテーブルから読み取ってコンソール出力を行います。
    public static void ReadAccountUsers()
    {
      using(ExampleDbContext context = new ExampleDbContext()) 
      {
        foreach( var userAccount in context.UserAccounts)
        {
          System.Console.WriteLine("氏名: " + userAccount.FirstName + userAccount.LastName);
          System.Console.WriteLine("TwitterID: " + userAccount.GetTwitterId());
          System.Console.WriteLine("-----");
        }
      }
    }
  }
}

コード内コメントにもあるように2人の UserAccount を追加します。1人は存在するTwitterIdを設定し、もう1人は存在しないTwitterIdを設定しています。後者の 存在しないTiwtterId を指定したユーザーは UserAccount.SetTwitterId() 内の処理により_validatedTwitterIdフィールド には値が反映されません。

実行する

dotnet run」コマンドでプログラムを実行します。
実行結果は、以下の通りです。

C:\Projects\dotNet\efCore11Example>dotnet run
氏名: ryuichidaigo
TwitterID: ryuichi111std
-----
氏名: fuseiaccount
TwitterID:
-----

SQL Management Studioでデータベーステーブルを確認した結果は以下の画面キャプチャになります。

f:id:daigo-knowlbo:20161124033704p:plain

.NET Core開発で project.json / .csproj に手動で参照追加する場合・・・

こんな事ないですか?また、初心者な人、悩む事ないですか?的な記事です。

プロジェクトへの「パッケージ参照(アセンブリ参照)追加」は、Visual Studio などの統合開発環境を使用しているとGUI操作のみで完了してしまいます。
しかし、CLIVisual Studio Codeを使って開発をしている場合、project.json もしくは .csproj ファイルに手動で参照の追加を記述する必要があります。

【補足】project.json と .csproj
2016.11.23現在、.NET Coreではバージョンによってプロジェクトファイルの形式をproject.json形式としているバージョンとMSBuildの.csproj形式としているバージョンが混在しています。今後は .csproj 形式に統一(移行)すると見られます。

利用したいクラスのヘルプ及び名前空間を調べる

例えば「HttpClientクラス」を利用したい、と考えたと想定します。
(ブログ記事などをググって得られたコードスニペットからクラス名と使い方は分かったけれど、名前空間と実装アセンブリが省略されている、といったケースをここでは想定しています。)

HttpClientはその名前からも想像できますが、「HTTPリクエストの送信・HTTPレスポンスの受信」をプログラムで処理する事ができます。

まず、以下のURLに「.NET CoreのAPI reference」が公開されています。

https://docs.microsoft.com/ja-jp/dotnet/core/api/index

f:id:daigo-knowlbo:20161123165802p:plain

左上のテキストボックス(Filterと書かれたテキストボックス)に「HttpClient」と入力します。
続けて、フィルタリング結果の中から「HttpClient」をクリックします。

f:id:daigo-knowlbo:20161123170001p:plain

HttpClientが属する名前空間は以下の赤枠部分になります。
プログラム中では、これをusingしましょう。

f:id:daigo-knowlbo:20161123170449p:plain

同ページの一番下の部分までスクロールしてみます。
「Assembly」という項目があり、ここでは「System.Net.Http.dll」となっています。

f:id:daigo-knowlbo:20161123170551p:plain

つまり、HttpClientクラスは「System.Net.Http名前空間に属するクラスであり、System.Net.Http.dllアセンブリに実装されている」という事が分かりました。

nugetパッケージの調査

では次に、System.Net.Httpのnugetパッケージを調べます。
ブラウザで以下のURLにアクセスします。

https://www.nuget.org/

これは、nugetのトップページになります。
ページ上部の「Search Packages」に「System.Net.Http」と入力しEnterキーをクリックします。
検索結果が表示され、一番上の項目が今回探していたものであると分かります。

f:id:daigo-knowlbo:20161123171147p:plain

対象のページに移動すると、対象のパッケージの最新バージョンのページが表示され、「対象パッケージが依存するフレームワークアセンブリのバージョン」「同パッケージの過去のバージョン履歴一覧」が表示されます。
という事で、今回は2016/11/23現在の最新バージョンである 4.3.0 を利用することにします。

参照(依存)設定の記述(project.json形式の場合)

まず、project.jsonの例は以下になります。project.jsonの場合、dependencies項目への追記となります。

--- project.json ---
...省略
  "dependencies": {
    "System.Net.Http": "4.3.0",
  },
...省略

dependencies配下のキー項目が”パッケージ名”、値項目が”バージョン番号”となります。

参照(依存)設定の記述(.csproj形式の場合)

.csprojの例は以下になります。→配下にタグを追記します。

--- .csproj ---
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
  
  ...省略

  <ItemGroup>

    <PackageReference Include="System.Net.Http">
      <Version>4.3.0</Version>
    </PackageReference>
  </ItemGroup>

  ...省略
</Project>

PackageReference要素のInclude属性が”パッケージ名”、配下のVersionタグ値が”バージョン番号”となります。

バージョンを明記しない

また、参照(依存)の記述の際にバージョン番号部分を「*(ワイルドカード)」とすると、その時点での最新バージョンを参照する事を意味します。
project.json / .csprojの例は以下になります。

--- project.json ---
...省略
  "dependencies": {
    "System.Net.Http": "*",
  },
...省略
--- .csproj ---
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
  
  ...省略

  <ItemGroup>

    <PackageReference Include="System.Net.Http">
      <Version>*</Version>
    </PackageReference>
  </ItemGroup>

  ...省略
</Project>

プログラムを書く

project.json / .csprojを整えた上で dotnet restore する事で、以下の様な HttpClient を利用したプログラムのビルドが通る様になります。

using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        HttpTest().Wait();;

        Console.WriteLine("Hello World!");
    }

    static async Task HttpTest()
    {
        using( HttpClient httpClient = new HttpClient() )
        {
            var response = await httpClient.GetAsync("http://www.microsoft.com/");
            var stringResponse = await response.Content.ReadAsStringAsync();

            Console.WriteLine(stringResponse);
        }
    }
}

おまけ - Entity Framework Core のAPI Referenceはこちらです

Entity Framework Core のAPI Referenceは以下になります。

Entity Framework Core API Reference | Microsoft Docs

会社のブログができたので・・そっちにもたまに投稿します

私の所属する会社の「開発者ブログ」なるものが立ち上がりました。
ということで、そっちにもたまに投稿します!

dev.knowlbo.co.jp

という宣伝投稿でした。

本ブログは .NET Core を軸に、会社のブログへの投稿は Xamarin などのモバイル系および業務的に得たTips的なものを投稿していこうかと思っています。

ひとまず、本日は”「Visual Studio for Mac」でXamarin Forms + ASP.NET Coreシステムを作る”という投稿を行ってみました。

dev.knowlbo.co.jp

ではよろしければ覗いてくださいー