「ComponentOne」の製品名称について
前回の記事では、製品名称を「ComponentOne Studio」と表記していましたが、2018年5月1日より「ComponentOne」に変更となるため、本稿では新しいものに表記を統一しています。名称が変更になるだけで、この2つは同一の製品です。
ASP.NET Coreは前述した通り、マルチプラットフォームで動作可能であると同時に、マルチプラットフォームでの開発方法が用意されています。グレープシティの 「ComponentOne for ASP.NET MVC」もまた、マルチプラットフォームでの利用をサポートしています。
そこで本稿では、以下の点について確認していきます。
- ASP.NET CoreのMac環境やLinux環境での主な開発方法
- グレープシティの「ComponentOne for ASP.NET MVC」をマルチプラットフォームで使用する方法
- NgnixやApacheを使用したホスト手順
なお、最初にお断りしておくと、グレープシティの 「ComponentOne for ASP.NET MVC」は、マルチプラットフォームによる運用利用はサポートしていますが、Mac/Linux環境での開発をサポート対象外としています。
そこで、筆者の興味によりComponentOne for ASP.NET MVCを使って開発を行ってみたところ、Mac環境やLinux環境で実際に開発できることを確認できました。今回はその手順を紹介します。
対象読者
- C#/ASP.NET MVCなどを使用したWebアプリの開発経験者
ASP.NET Core 2.0を使用したWebアプリの開発については、以下の記事も参考にしてください。
- ASP.NET Coreの概要を理解してセットアップしよう(CodeZine)
- 「ASP.NET Core 2.0」の変更点とインストール方法を知ろう(CodeZine)
- ASP.NET Core 2.0でRazor Pagesアプリケーションを作ろう(CodeZine)
必要な環境
- Windows:Visual Studio 2017のWeb開発ワークロードをインストールしていること
- Mac:Visual Studio for Macをインストールしていること
- Linux:Visual Studio Codeおよび .NET Core SDKのインストールしていること
本稿で使用した.NET Core SDKのバージョンは2.1.103です。また、本稿のサンプルコードを実行する場合は、ComponentOne for ASP.NET MVCが必要です。
ComponentOne for ASP.NET MVCは、以下のページの矢印にあるリンクからトライアル版がダウンロードできます。
今回紹介する方法は、ComponentOneのサイトにログインする必要があります。トライアル版ユーザーでも試すことができるので、事前にユーザー登録を行っておくことをお勧めします。
今回利用するコンポーネント
利用するコンポーネントは、基本的に前回と同じ以下のコンポーネントを使用します。
FlexChart for ASP.NET MVC
Guages for ASP.NET MVC
InputNumber(Input for ASP.NET MVC)
InputDateTime(Input for ASP.NET MVC)
MultiAutoComplete(Input for ASP.NET MVC)
各コンポーネントの説明は前回の記事に記載したため、本稿では割愛します。
今回は、これらのコンポーネントと、ASP.NET Core 2.0の新機能であるRazor Pagesを使用してサンプルを作成します。Razor Pagesの解説は、CodeZineの掲載の記事で解説されているため、本稿では概要のみ説明します。
Razor Pagesと階層分割アプローチ
Razor PagesはASP.NET Core 2.0の新機能で、「Model-View-ViewModel」という階層分割アプローチを取り入れたアーキテクチャーパターンに沿ったフレームワークです。
はじめに、Model-View-ViewModelのアーキテクチャーパターンや階層分割アプローチについて、少し触れておきます。
ソフトウェアの規模
アプリケーションを作成時に開発者が遭遇する問題のひとつに、「ソフトウェアの規模」の問題があります。
ソフトウェアの規模が小さいアプリケーションは、比較的見通しもよく、可読性が高いことから取り扱いもしやすいため、プログラミングの際に大きな問題となることは多くないでしょう。
しかし、実際にソフトウェアで取り扱う問題は単純なものばかりではありません。 加えて「複雑な問題を機械的に間違いなく実施することができる」ことは、ソフトウェアを作成する動機のひとつでもあります。
ソフトウェアで複雑な問題を取り扱うと、その複雑さに比例してソフトウェアの規模も大きくなります。
規模が大きいソフトウェアを取り扱う場合、何も考慮せずソフトウェアを作成すると、元々の問題の複雑さに加えて、ソフトウェアの複雑さが加わる場合があります。
その結果、ソフトウェアの可読性・保守性が低下し、ひいては品質の低下につながることになります。
つまり、複雑なソフトウェアを作成する場合には、それに対応するための対処方法が必要になります。
関心事の分離
このような複雑な問題に対するひとつのアプローチとして、「関心事の分離(Separation of Concerns、略してSoCと呼びます)」があります。
SoCでは、ソフトウェアが解決したい問題を個々の問題(関心)毎に分離し、構成する手法です。つまり、「大きな問題」を「小さな複数の問題」に分割して扱うことで、一つひとつの問題の複雑さを軽減し、取り扱いやすくするという設計原則です。
ただし、やみくもに分割すると、分割の粒度があいまいになることで、問題の粒度が不揃いになり、問題の内容がかえって分かりづらくなったりします。その結果、ソフトウェアの全体像が見えづらくなったり、複雑さが増してしまう場合があります。
分離へのアプローチ
そこで、これらの分割の手法としてよく用いられるのが「階層化アプローチ(Layered approach)」です。
「階層化アプローチ」 は、責任の分割を主な目的としています。ソフトウェアが扱う問題を特定の役割を持つ階層に分割します。分割した各階層は、他の階層への「インターフェース」、つまり窓口だけを取り決めして、その結果を保証します。
各階層とやりとりを行う「インターフェース」と、「インターフェース」でどのような「データ」を受け渡すかを取り決めます。
それ以外の各階層内で行われる処理方法や、階層の内部で使用されるデータについては、他の階層が一切関知しないものとします。
それにより、各階層内部は処理やデータの影響範囲を限定されるため、ソフトウェア保守時に掌握すべき項目が減るため、可読性・保守性の向上につながります。
階層化アプローチの基準
実際にどのような階層に分割すればよいかの基準のひとつとして、マーチンファウラーが提唱した「プレゼンテーションとドメインの分離(Presentation Domain Separation、略してPDSと呼びます)」があります。
PDSでは、ソフトウェアの「ユーザーインターフェース」部分と「その他の機能」部分を分割することで、以下のメリットを享受できるとしています。(引用:Martin Fowler’s Bliki (ja)日本語訳)
- プレゼンテーションロジックとドメインロジックが分かれていると、理解しやすい
- 同じ基本プログラムを、重複コードなしに、複数のプレゼンテーションに対応させることができる
- ユーザーインターフェースはテストがしにくいため、それを分離することにより、テスト可能なロジック部分に集中できる
- スクリプト用のAPIやサービスとして外部化するためのAPIを楽に追加できる(選択可能なプレゼンテーション部分で見かける)
- プレゼンテーション部分のコードは、ドメイン部分のコードと違ったスキルと知識が必要
ここにあるような分割のアプローチを具現化したアーキテクチャーパターンが、ASP.NETでも利用されているModel-View-Controller(MVC)であったり、これから利用するModel-View-ViewModel(MVVM)などになります。
Model-View-ViewModel
Razor Pagesは、階層化アプローチであるModel-View-ViewModelを用いたフレームワークです。ここで、Model-View-ViewModelについても、少し触れておきたいと思います。
Model-View-ViewModelでは、プレゼンテーション部分をView-ViewModel、ドメイン部分をModelに分割して構成します。
そして、ViewとViewModelは「データバインディング」のみで関連付けを行うことで、ユーザーインターフェースとデータの分離を実現するアーキテクチャーパターンです。
MVVMは元々、Windows Presentation Foundation(WPF)やUniversal Windows Platform(UWP)などで利用されていたパターンです。
WPFやUWPは、ユーザーインターフェースの記述を「XAML」と呼ばれるXMLベースのマークアップ言語で行います。XAMLは、データとユーザーインターフェースの要素を関連付けして、値の同期を実現する、データバインディング機能を持ちます。このデータバインディング機能を使用することで、ViewとViewModelの分離を実現していました。
このとき、ViewModelではViewに表示するために必要なデータを「プロパティ」で表現し、Viewはそのプロパティを参照することでユーザーインターフェースの表現に関連付けました。
以下の例は、実際にデータバインディングを行っているViewのXAMLとViewModelのC#コードです。ここでは、ViewとViewModelが分離されていることを示す例なので、コードの内容は理解できなくてもかまいません。
<Grid> <Grid.DataContext> <local:SampleViewModel/> <!-- ViewModelクラスのインスタンスを設定 --> </Grid.DataContext> <TextBox Text="{Binding CustomerName}"/> <!-- ViewModelクラスのプロパティにBinding --> </Grid>
public class SampleViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private string customerName; // Bindingするためのプロパティ public string CustomerName { get => customerName; set { customerName = value; RaisePropertyChanged(); } } protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
この分離によって、Viewに表示されるべきデータが正しく表示できない原因は「正しくデータバインディングが設定されていない」または「ViewModel以降の階層が正しく実装されていない」の2点に限定することができました。
さらにViewModelクラスは、上記のように単独のC#クラスであるため、その他のModelクラスと同様に単体テストの作成も行いやすくなります。
つまり、View以外のいずれの階層でも単体テストを行うことができるようになったことで、回帰テストが実施しやすくなり、品質の向上に貢献します。
Razor PagesとModel-View-ViewModel
繰り返しになりますが、Razor Pagesは、このModel-View-ViewModelのアーキテクチャーモデルに倣ってWebアプリの開発が行えるフレームワークです。
Model-View-ViewModelに沿って、Razor Pagesではcshtml(Razor構文によるView)-cshtml.cs(PageModelクラスを継承した単一のクラス)-Modelクラスで構成します。
ここで、RazorPagesのテンプレートプロジェクトを見てみましょう。Razor Pagesのプロジェクトテンプレートの作成は、Visual Studioの新規プロジェクトの作成で[ASP.NET Core Web アプリケーション]を選択するか、dotnetコマンドラインで行えます。
dotnetコマンドライン CLIで行う場合は、任意のフォルダー内で以下のコマンドを実行することで作成されます。
dotnet new razor
作成された実際のプロジェクトは下図の通りです。
Razor Pagesのプロジェクトテンプレートは、ASP.NET Core MVCのプロジェクトに比べて、フォルダーが「Pages」のみとシンプルです。
「Pages」フォルダーの中には、いくつかのcshtmlファイルが入っていますが、「About.cshtml」「Contact.cshtml」「Error.cshtml」「Index.cshtml」の4ファイルには、関連する「*.cshtml.cs」ファイルが紐づけされています。
この「*.cshtml.cs」ファイルが、ViewModelの役割を担う「PageModel」ファイルです。
Razor PagesによるWebページの作成
Razor PagesによるWebページの作成方法をまとめると、以下の通りになります。
- Viewである「*.cshtml」は、Razor構文で記述する
-
Razor Pagesのcshtmlファイルの先頭行にはRazor Pagesである宣言として
@page
を記述する(先頭にないと404で表示できなくなる) -
ViewModelは、cshtmlに対応する
PageModel
クラスを継承する - PageModelクラスには、cshtmlにデータバインドするためのpublicプロパティを追加する
-
GET、POSTなどでデータ送信が必要なる項目のプロパティは、
BindProperty
属性を付与する
実際にASP.NET Coreによるテンプレートプロジェクトを少し加工し、「Index.cshtml」と「Index.cshtml.cs」を修正したソースが以下です。
@page @model IndexModel @{ ViewData["Title"] = "Home page"; } <!-- Index.cshtml --> <form method="post"> <div class="row"> <div class="col-md-12"> <h1>@Model.Message</h1> </div> </div> <div class="row"> <div class="col-md-3">Your Name</div> <div class="col-md-9"> <input type="text" asp-for="UserName" /> </div> </div> <div class="row"> <div class="col-md-12"> <input type="submit" /> </div> </div> </form>
// Index.cshtml.cs public class IndexModel : PageModel { // 表示だけならば通常プロパティでOK public string Message { get; set; } // 送信時にデータを格納したい場合はBindProperty属性を付与 [BindProperty] public string UserName { get; set; } // GET時に行う処理 public void OnGet() { Message = "Hello, world"; UserName = string.Empty; } // POST時に行う処理 public void OnPost() { Message = $"Hello, {UserName}"; } }
使用するWebアプリについて
今回のサンプルとして利用するWebアプリは、ASP.NET CoreのRazor Pagesプロジェクトのテンプレートを再利用して作成します。
ここでは、まずWindows環境での作成方法の説明を行います。
Windows環境でRazor Pagesアプリを作成する場合、Visual Studio 2017を起動し、[新しいプロジェクトの作成]から[ASP.NET Core Webアプリケーション]を選択します。
プロジェクト名を設定し(サンプルでは「C1RazorPagesApp」とします)、OKボタンをクリックします。
続けて[新しいASP.NET Core Webアプリケーション]が表示されます。
ここでは無印の[Web アプリケーション]のアイコンを選択してOKボタンをクリックします。
Razor PagesからComponentOne for ASP.NET MVCを使用する方法について
Razor PagesにComponentOne for ASP.NET MVCを使用する場合は、NuGetからパッケージのダウンロードを行います。そのため、インターネットの接続できる環境でパッケージの追加を行う必要があります。
パッケージの追加
ソリューションエクスプローラーから、プロジェクトを右クリックし、コンテキストメニューを表示します。プロジェクトのコンテキストメニューが表示されたら[NuGet パッケージの管理]をクリックします。
クリックするとNuGetパッケージの管理画面が表示されます。
パッケージの追加は以下の手順で行います。
- 右側上部にある[参照]タブ(図の(1))に切り替えます。
- 左側上部にある[パッケージソース](図の(2))を先ほど追加した「GrapeCity」に切り替えます。
- 右側上部にあるパッケージの検索ボックス(図の(3)にあるテキストボックス)に「C1.AspNetCore.Mvc.ja」を入力し、Enterキーを押下します。
正しく検索が行われると、図のようにパッケージの表示が行われます。パッケージをクリックし、プロジェクトにインストールします。
Modelクラス
ASP.NET CoreのRazor PagesプロジェクトのテンプレートにはModel用のフォルダーが用意されていません。そのため最初に、プロジェクトに「Models」フォルダーを作成します。
作成した「Models」フォルダーの中に、以下のSampleModel
クラスを作成します。
using System; using System.Collections.Generic; using System.Linq; namespace C1RazorPagesApp.Models { // Chart用サンプルエンティティ public class DailySale { public DateTime Date { get; set; } public int? TotalSales { get; set; } public int? AsiaSales { get; set; } public int? JapanSales { get; set; } } // サンプルデータ作成用Modelクラス public class SampleModel { // チャートに表示する値は乱数から作成 private static readonly Random rand = new Random(0); public static IEnumerable<DailySale> GetSales() => Enumerable.Range(0, 92) .Select(index => new DailySale { Date = new DateTime(DateTime.Today.Year, 1, 1).AddDays(index), JapanSales = rand.Next(0, 100), AsiaSales = rand.Next(100, 200), TotalSales = rand.Next(200, 300) }); // Guageに表示するデータ public static int GuageValue { get; } = 30; // InputNumberのデータ public static int InputValue { get; } = 567; // InputDatetimeのデータ public static DateTime InputDatetime { get; set; } = DateTime.Now; // MultiAutoCompleteのデータ public static List<string> Todofuken { get; } = new List<string> { "Hokkaido", "Aomori", "Iwate", "Miyagi", "Akita", "Yamagata", "Fukushima", "Ibaraki", "Tochigi", "Gunma", "Saitama", "Chiba", "Tokyo", "Kanagawa", "Niigata", "Toyama", "Ishikawa", "Fukui", "Yamanashi", "Nagano", "Gifu", "Shizuoka", "Aichi", "Mie", "Shiga", "Kyoto", "Osaka", "Hyogo", "Nara", "Wakayama", "Tottori", "Shimane", "Okayama", "Hiroshima", "Yamaguchi", "Tokushima", "Kagawa", "Ehime", "Kochi", "Fukuoka", "Saga", "Nagasaki", "Kumamoto", "Oita", "Miyazaki", "Kagoshima", "Okinawa", }; } }
Startupクラス
プロジェクトルートにある「Startup.cs」ファイルを開き、Startupクラスを修正します。
.NET Core SDKのバージョン 2.0.5以降では、以下の通りルート定義の修正を行う必要があります。Razor PagesでComponentOne for ASP.NET MVCを使用する場合は、この修正が必要です。
// 2.0.5以前はこちら //app.UseMvc(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller}/{action=Index}/{id?}"); });
ライセンスの追加
ComponentOne for ASP.NET MVCを使用する場合、ライセンス情報の追加が必要になります。ライセンス情報の追加はソリューションエクスプローラーから行います。
ComponentOne for ASP.NET MVCのインストールが完了すると、ソリューションエクスプローラーからライセンス情報の追加を行うことができます。
ソリューションエクスプローラーに表示される[ソリューション]を右クリックします。右クリックすると表示されるコンテキストメニューの中に[C1 ASP.NET Coreランタイムライセンスの生成]というメニューが追加されています。表示されたコンテキストメニューから[C1 ASP.NET Coreランタイムライセンスの生成]をクリックするとライセンスの生成が始まります。
ライセンスの生成が成功すると下図のダイアログが表示されます。
ライセンス情報はプロジェクトのルート直下にある「Startup.cs」のコンストラクタに「C1.Web.Mvc.LicenseManager.Key」へのキー設定が追加されます。
public Startup(IConfiguration configuration) { Configuration = configuration; C1.Web.Mvc.LicenseManager.Key = "(埋め込まれたライセンスキー)"; }
なお、Mac環境/Linux環境ではLicenseキーの追加方法が異なります。各環境においてのライセンスの追加方法は、それぞれの開発手順の解説の中で説明します。
ファイル「_ViewImports.cshtml」
「_ViewImports.cshtml」は、Viewの中でアプリケーションを通して、共通的に利用する宣言や定義などを追加するファイルです。
このファイル「_ViewImports.cshtml」は、「Pages」フォルダーにあります。ここでは以下のように利用する名前空間やTagHelperの設定などを追加します。
@using C1RazorPagesApp @namespace C1RazorPagesApp.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, C1.AspNetCore.Mvc @addTagHelper *, C1.AspNetCore.Mvc.FlexSheet
ファイル _Layout.cshtml
「_Layout.cshtml」は、Viewで使用するHTMLのひな形を定義するファイルです。
ComponentOne for ASP.NET MVCを使用する場合、各コントロールで使用するCSSやScriptの定義を追加する必要があります。ComponentOne for ASP.NET MVCでは、CSSやScriptの定義を展開するTag Helperが用意されています。
ここでは「Pages」フォルダーにあるファイル「_Layout.cshtml」を開いて、以下のようにHEADタグ内にTag Helperを追加します。
<c1-styles></c1-styles> <c1-scripts> <c1-basic-scripts></c1-basic-scripts> </c1-scripts>
PageModelクラス
PageModelクラスは「Pages」フォルダーにある「Index.cshtml.cs」を加工します。IndexModelクラスに、以下の通りプロパティの追加を行います。
public class IndexModel : PageModel { // チャートに表示するデータ public IEnumerable<DailySale> Sales { get; set; } = SampleModel.GetSales(); // Guageに表示するデータ public int GuageValue { get; set; } = SampleModel.GuageValue; // InputNumberのデータ public int InputValue { get; set; } = SampleModel.InputValue; // InputDatetimeのデータ public DateTime InputDatetime { get; set; } = SampleModel.InputDatetime; // MultiAutoCompleteのデータ public List<string> Todofuken { get; set; } = SampleModel.Todofuken; [BindProperty] public string SelectedTodofuken { get; set; } public void OnGet() { } }
View(Index.cshtml)
ViewであるIndex.cshtmlも「Pages」フォルダーにあるので、以下の通り加工します。
@page @model IndexModel @{ ViewData["Title"] = "Home page"; } <link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet"> <Style> .chart-title { background-color: darkblue; } .guage-title { background-color: darkgreen; } .number-title { background-color: darkcyan; } .calendar-title { background-color: darkred; } .autocomplete-title { background-color: darkgoldenrod; } .title { color: white; } h2 { font-size: 30px; font-weight: 700; line-height: 30px; margin-bottom: 50px; text-transform: uppercase !important; font-family: Montserrat,'Helvetica Neue',Helvetica,Arial,sans-serif; } .wrap { display: flex; display: -moz-box; display: -ms-flexbox; display: -webkit-box; -moz-flex-wrap: wrap; -ms-flex-wrap: wrap; -o-flex-wrap: wrap; -webkit-flex-wrap: wrap; flex-wrap: wrap; } .control-item { margin: auto; } </Style> <div class="row wrap"> <div class="col-md-4 chart-title title"> <h2>FlexChart</h2> </div> <div class="col-md-8 control-item"> <c1-flex-chart id="flexChart" binding-x="Date" chart-type="Area"> <c1-items-source source-collection="@Model.Sales" /> <c1-flex-chart-series binding="TotalSales" name="総売り上げ" /> <c1-flex-chart-series binding="AsiaSales" name="アジアの売り上げ" /> <c1-flex-chart-series binding="JapanSales" name="日本の売り上げ" /> </c1-flex-chart> </div> </div> <div class="row wrap"> <div class="col-md-4 guage-title title"> <h2>Guage</h2> </div> <div class="col-md-8 control-item"> <c1-radial-gauge min="0" max="100" start-angle="-20" sweep-angle="220" width="500" height="200" show-text="None" value="@Model.GuageValue"> <c1-gauge-range min="0" max="40" color="red" /> <c1-gauge-range min="40" max="80" color="yellow" /> <c1-gauge-range min="80" max="100" color="green" /> </c1-radial-gauge> </div> </div> <div class="row wrap"> <div class="col-md-4 number-title title"> <h2>InputNumber</h2> </div> <div class="col-md-8 control-item"> <c1-input-number value="@Model.InputValue" show-spinner="true" step="10" format="n0" min="0" max="1000" placeholder="1~1000までの値を入力してください。"> </c1-input-number> </div> </div> <div class="row wrap"> <div class="col-md-4 calendar-title title"> <h2>InputDateTime</h2> </div> <div class="col-md-8 control-item"> <c1-input-date value="@Model.InputDatetime" min="DateTime.Today.AddYears(-1)" max="DateTime.Today.AddYears(1)"> </c1-input-date> </div> </div> <div class="row wrap"> <div class="col-md-4 autocomplete-title title"> <h2>MultiAutoComplete</h2> </div> <div class="col-md-8 control-item"> <c1-multi-auto-complete selected-index="12" max-selected-items="5" selected-item="@Model.SelectedTodofuken"> <c1-items-source source-collection="@Model.Todofuken" /> </c1-multi-auto-complete> </div> </div>
macOS環境の開発
それでは、macOS環境での開発について説明します。macOSにおけるASP.NET Coreの開発環境には以下があります。
- Visual Studio for Mac(IDE、Microsoft)
- Visual Studio Code(エディタ、Microsoft)
- JetBrains Rider(IDE、JetBrains)
本稿では、Visual Studio for Macを利用した開発手順を紹介します。
Visual Studio for Macの起動
Visual Studio for Macを起動すると[スタートページ]を表示します。
[新しいプロジェクト]をクリックすると[テンプレート選択画面]を表示します。
今回はRazor Pagesを使用したアプリの開発なので、[.NET Core]の[アプリ]のテンプレートの中から[ASP.NET Core Webアプリ]を選択します。
[次へ]ボタンをクリックすると、プロジェクト名やソリューション名など入力や設定を行う[構成画面]を表示します。
環境に合わせてプロジェクト名やソリューション名を入力や場所の設定を行います。今回は、プロジェクト名を「C1RazorPagesApp」とします。
[作成]ボタンをクリックするとRazor Pagesアプリのプロジェクトを作成し、構成ファイルをダウンロードします。すべての設定が完了するとコードエディタウインドウを表示します。
今回のサンプルアプリは、Modelクラスを追加するため、まず、Modelクラスを格納する「Models」フォルダーを追加します。
コードエディタの左に表示されるソリューションエクスプローラーにある[C1RazorPages]プロジェクトを右クリックし、コンテキストメニューを表示します。
コンテキストメニューから[追加]→[新しいフォルダー]をクリックします。
Modelクラスを格納するためのフォルダーなので「Models」と命名します。そして、Modelsフォルダーにモデルクラスを追加します。
次に、ソリューションエクスプローラーからModelsフォルダーを右クリックして、コンテキストメニューを表示します。
コンテキストメニューから[追加]→[新しいファイル]を選択すると[新しいファイルウインドウ]が開きます。
[General]→[空のクラス]を選択後、名前を「SampleModel」と設定します。その後、[新規]ボタンをクリックします。
SampleModelクラスが作成されるとコードエディタに表示されるので、前ページに記載したSampleModelクラスの内容を入力します。
ComponentOne for ASP.NET MVCのパッケージをインストール
冒頭でも触れましたが、ComponentOne for ASP.NET MVCは、Mac環境での開発はサポート対象外です。お試しになる場合は自己責任でお願いします。
Mac環境でComponentOne for ASP.NET MVCのコントロールを使用する場合、ViewやPageModelを作成する前にNuGetからパッケージをダウンロード/インストールしなくてはいけません。
Visual Studio for MacからNuGet経由でインストールする場合も、パッケージソースの追加を行う必要があります。
Visual Studio for Macにおけるパッケージソースの追加
パッケージソースの追加は、メニューから[プロジェクト]→[NuGet パッケージの追加(P)]をクリックします。
[パッケージの追加(P)]をクリックすると[パッケージの追加ウインドウ]が表示されるので、左上部にあるパッケージソースのドロップダウンから[ソースの構成]を選択します。
[ソースの構成]を選択すると[ユーザー設定]ウインドウを開き、[NuGet]→[ソース]の設定画面になります。
パッケージソースの追加を行う行う場合は、右ペインの下にある[追加]ボタンをクリックします。[追加]ボタンをクリックすると[パッケージソースの追加ウインドウ]が開きます。
[パッケージソースの追加ウインドウ]では以下の項目を設定します。
項目 | 設定値 |
---|---|
名前 | GrapeCity |
場所 | http://nuget.c1.grapecity.com/nuget/ |
設定が完了したら[ソースの追加]ボタンをクリックします。
正しく追加が行われると、パッケージソースのドロップダウンに「GrapeCity」が追加されます。
ComponentOne for ASP.NET MVC パッケージのインストール
今回の記事は、前回の記事と同様のコントロールを使用しているので、前回の記事と同じパッケージを追加します。
[パッケージソースの追加]が終わり、[OK]ボタンをクリックすると[NuGetパッケージの追加画面]に戻ります。
ComponentOne for ASP.NET MVCのパッケージをインストールするには、パッケージソースのドロップダウンを「GrapeCity」に切り替えます。
次に[NuGetパッケージの追加画面]のウインドウ右上部にある検索ボックスから「C1.AspNetCore.Mvc」を検索します。
検索結果の一覧から「C1.AspNetCore.Mvc.ja」と「C1.AspNetCore.Mvc.FlexSheet.ja」の2つのパッケージの前にあるチェックボックスにチェックします。チェックが終わったら[パッケージを追加]ボタンをクリックします。
「_Layout.cshtml」「_ViewImports.cshtml」「Index.cshtml」「Index.cshtml.cs」および「Startup.cs」をコーディング
プロジェクトの中にある「Pages」、フォルダーの中にある「_Layout.cshtml」「_ViewImports.cshtml」「Index.cshtml」「Index.cshtml.cs」、プロジェクトルートにある「Startup.cs」を前ページの通りコーディングします。
License.csの追加
ライセンス情報の追加は、ComponentOneのサイトにログインすることで、ライセンスキーの発行を行うことが可能です。
ComponentOneのログイン画面から新規ユーザーの登録を行います。既に登録している場合は、ログインします。
ログイン後、表示される[アカウントサービス画面]から[ASP.NET Core ライセンス生成]のリンクをクリックします。
シリアルナンバーに有効なシリアルまたはトライアル版を選択し、アプリケーション名に任意の名前を設定します。設定が終わったら[生成]ボタンをクリックします。
[生成]ボタンをクリックし、しばらくするとライセンスキーを含むLicenseクラスのテキストが表示されます。
表示されたライセンスキークラスのテキストを全選択し、コピーします。
Visual Studio for Macに戻り、プロジェクトにLicense.cs
クラスを追加し、コピーしたテキストを貼り付けます。
続けて「Startup.cs」にあるStartup
クラスのコンストラクタに、ライセンスマネージャーへのキーの設定を追加します。
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; C1.Web.Mvc.Sheet.LicenseManager.Key = License.Key; C1.Web.Mvc.LicenseManager.Key = License.Key; } // 以下省略
ライセンスキーの設定後、コマンドラインでdotnet run
で実行します。
コマンドラインに表示されたURLをブラウザで確認すると、下図のように表示されます。
Linux環境の開発
それでは、Linux環境の開発方法を説明します。Linux環境における開発環境には以下のツールがあります。
- Visual Studio Code(エディタ、Microsoft)
- JetBrains Rider(IDE、JetBrains)
本稿では、高機能エディタであるVisual Studio Codeと.NET Core コマンドラインインターフェース+NuGet コマンドラインインターフェース(CLI)を組み合わせて開発します。
NuGet CLIは、.NET Frameworkで開発されているツールです。Mac/Linux環境でNugetを実行するためには、Linux環境で動作する.NET Framework互換の実装の「Mono」を使用します。
各CLIツールのインストール
Linuxの場合は、利用するディストリビューションやバージョンなどによってインストール方法が異なるため、Microsoftのサイトで説明されているインストール方法を参照していただき、以下のツールをインストールします。
- .NET Core SDK ダウンロードサイト
- Linux における .NET Core の前提条件(インストール手順が掲載されています)
- Install Mono
- NuGet CLI ダウンロード(Windows x86 CommandLine)
- NuGet クライアント ツールのインストール
筆者はCentOS 7を使用して環境を作成しました。設定はリリースした.NET Core SDKのバージョンによって変化することがあるため、必ず上記の手順を確認してください。
参考までに、筆者が本記事執筆時に実施したインストール手順は以下の通りです。
.NET Core SDKのインストール
.NET Coreをインストールするため、最初にMicrosoftの署名を登録します。
sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
続けて、Microsoft製品のフィードを追加します。
sudo sh -c 'echo -e "[packages-microsoft-com-prod]\nname=packages-microsoft-com-prod \nbaseurl=https://packages.microsoft.com/yumrepos/microsoft-rhel7.3-prod\nenabled=1\ngpgcheck=1\ngpgkey=https://packages.microsoft.com/keys/microsoft.asc" > /etc/yum.repos.d/dotnetdev.repo'
次に、.NET Core SDKのインストールします。
sudo yum update sudo yum install libunwind libicu sudo yum install dotnet-sdk-2.0.0
任意の場所から動作できるようPATHの追加を行います。
export PATH=$PATH:/usr/share/dotnet
以上で、.NET Core SDKのインストールは完了です。
コマンドラインからdotnet
を実行して、下図の通り表示されればインストールは成功です。
Monoのインストール
Monoのインストールは以下のコマンドラインで行います。
rpm --import "http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF" su -c 'curl https://download.mono-project.com/repo/centos7-stable.repo | tee /etc/yum.repos.d/mono-centos7-stable.repo' yum install mono-devel
コマンドラインからmono
を実行して、下図の通り表示されればインストールは成功です。
NuGetのインストール
Nugetのインストールは以下のコマンドラインで行います。
# 最新のnugetをDownloadして `/usr/local/bin`に格納 sudo curl -o /usr/local/bin/nuget.exe https://dist.nuget.org/win-x86-commandline/latest/nuget.exe # Give the file permissions to execute sudo chmod 755 /usr/local/bin/nuget.exe
これで、以下のコマンドラインでNuGetが利用できます。
mono /usr/local/bin/nuget.exe
NuGet CLIを利用しやすいように、「~/.base_alias」や「~/.bash_profile」などに以下のaliasのスクリプトを登録します。
alias nuget="mono /usr/local/bin/nuget.exe"
コマンドラインからnuget
を実行して、下図の通り表示されればインストールは成功です。
Razor Pagesアプリの作成
Linux環境における.NET Coreの開発は、主に.NET Core CLIを使用して開発を行います。.NET Core CLIの基本的な利用方法は、WindowsやmacOSでも同じです。今回はRazor Pagesのテンプレートを使って開発を行う手順を紹介します。
プロジェクトの作成
まず、任意のディレクトリを作成し、.NET Core CLIでプロジェクトファイルを作成します。
Razor Pagesのプロジェクトの作成は以下のコマンドラインを実行して作成します。
dotnet new razor
Modelクラスの追加
プロジェクトの作成を行うと、ディレクトリ内にRazor Pagesのひな形プロジェクトがダウンロードされます。
これ以降の作業は、Visual Studio Codeを利用して行います。Visual Studio Codeを起動し、プロジェクトのディレクトリを開きます。表示されたディレクトリから、モデルクラスを格納する「Models」フォルダーを追加します。
続けて、「Models」フォルダーの中に、前ページを参考に「SampleModel.cs」ファイルを作成し、SampleModel
クラスの追加を行います。
ComponentOne for ASP.NET MVC パッケージのインストール
ComponentOne for ASP.NET MVCのコントロールを使用する場合、ViewやPageModelを作成する前にNuGetからパッケージをダウンロード/インストールする必要があります。
パッケージソースの追加
NuGetコマンドラインからNuGet経由でインストールする場合も、Windowsと同様にパッケージソースの追加を行う必要があります。パッケージソースの追加はNugetの環境設定の構成を編集することによって行います。
具体的な手順としては、Nugetおよび.NET Core SDKインストール時に追加されたNuget.Configを編集します。Nuget.Configは、Mac/Linux環境では「~/.Nuget/Nuget.Config」に配置されています。
Nuget.ConfigファイルをVSCodeで開いて、以下のようにpackageSources
タグにGrapeCityのレポジトリを追記します。
<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> <!-- 以下の行を追記 --> <add key="GrapeCity" value="http://nuget.c1.grapecity.com/nuget/" /> </packageSources> </configuration>
追記後、ファイルを保存します。
パッケージの追加
今回の記事は、前回の記事と同様のコントロールを使用しているので、前回の記事と同じパッケージを追加します。パッケージの追加は、プロジェクトファイル(拡張子がcsprojのファイル)を編集して行います。
VSCodeでプロジェクトファイルを開いて、PackageReference
要素が含まれるItemGroupに以下の通り、ComponentOne for ASP.NET MVCのPackageReference
タグを2つ追記します。
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp2.0</TargetFramework> </PropertyGroup> <ItemGroup> <!-- ここから追加 --> <PackageReference Include="C1.AspNetCore.Mvc.FlexSheet.ja" Version="1.0.20173.143" /> <PackageReference Include="C1.AspNetCore.Mvc.ja" Version="1.0.20173.143" /> <!-- ここまで追加 --> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> </ItemGroup> <ItemGroup> <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" /> </ItemGroup> </Project>
「_ViewImports.cshtml」「_Layout.cshtm」「Index.cshtml」「Index.cshtml.cs」および「Startup.cs」をコーディング
プロジェクトの中にある「Pages」、フォルダーの中にある「_ViewImports.cshtml」 「_Layout.cshtm」「Index.cshtml」「Index.cshtml.cs」、プロジェクトルートにある「Startup.cs」を前ページの通りコーディングします。
License.csの追加
ライセンス情報の追加はComponentOneのサイトにログインすることで、ライセンスキーの発行を行うことが可能です。
ComponentOneのログイン画面から新規ユーザーの登録を行います。既に登録している場合はログインします。
ログイン後、表示される[アカウントサービス画面]から[ASP.NET Core ライセンス生成]のリンクをクリックします。
シリアルナンバーに有効なシリアルまたはトライアル版を選択し、アプリケーション名に任意の名前を設定します。終わったら[生成]ボタンをクリックします。
[生成]ボタンをクリックし、しばらくするとライセンスキーを含むLicenseクラスのテキストが表示されます。
表示されたライセンスキークラスのテキストを全選択し、コピーします。
Visual Studio for Macに戻り、プロジェクトにLicense.cs
クラスを追加してコピーしたテキストを貼り付けます。
続けて「Startup.cs」にあるStartup
クラスのコンストラクタに、ライセンスマネージャーへのキーの設定を追加します。
他に利用しているコントロールがある場合は、必要なライブラリのライセンスマネージャーにライセンスキーを同様に追加します。
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; C1.Web.Mvc.Sheet.LicenseManager.Key = License.Key; C1.Web.Mvc.LicenseManager.Key = License.Key; } // 以下省略
ライセンスキーの設定後、コマンドラインでdotnet run
で実行します。
コマンドラインに表示されたURLをブラウザで確認すると下図のように表示されます。
Linux環境のホスティング
ASP.NET Coreを使用したWebアプリは、.NET Core上で動作するKestrelというWebサーバー機能を持ち、独立したコンソールアプリとして実行します。
ただし、Kestrelにはアプリ実行のための基本的な機能しか持たないため、Webサーバーが持つセキュリティ機能や冗長化のための機能、リクエストのキャッシュなど多くのWebサーバーが持つ数々の機能を持ちません。
ホスティングする場合は、各プラットフォームの稼働するWebサーバー機能などによってリクエストを受けて、リクエストをASP.NET Coreアプリに転送する、いわゆるリバースプロキシーとして設定し、Kestrelが持たないWebサーバー機能を補完します。
本記事では、Linux環境のWebサーバー機能である「Ngnix」と「Apache」のリバースプロキシー機能を使用して行うホスティング方法をご紹介します。なお、今回ホスティングするLinux環境には、あらかじめ.NET Core SDKのインストールを行っています。
Ngnix によるホスティング
Nginx(エンジンエックスと発音します)は、オープンソースのWebサーバーです。Apacheほど高機能ではありませんが、処理性能/並列処理/メモリ使用量が小さく、パフォーマンスに優れていることが特徴で、LinuxだけではなくWindowsやMacでも動作します。
今回は、NgnixをCentOS 7にインストールし、Ngnixのリバースプロキシー機能を利用してASP.NET Coreアプリのホスティングします。
Nginxのインストール方法は既に多くのサイトで既に紹介されているため、ここでは割愛しますが、参考までに筆者がCentOS 7で行った手順を記しておきます。
Nginxのレポジトリの登録
Nginxが公開しているyumレポジトリの追加を行います。
yumレポジトリの追加は、Nginxの公式サイトに記載のある手順で以下の通り行います。
root権限を持つユーザーで、「/etc/yum.repos.d/nginx.repo」ファイルを作成し、以下の通り内容を記述します。
[nginx] name=nginx repo baseurl=http://nginx.org/packages/OS/OSRELEASE/$basearch/ gpgcheck=0 enabled=1
この内容を保存して、ファイルが正常に作成されていることを確認します。
# ls -l /etc/yum.repos.d/nginx.repo -rw-r--r--. 1 root root 108 Mar 21 11:35 /etc/yum.repos.d/nginx.repo
Nginxのインストール
レポジトリ情報の保存が終わると、いよいよインストールです。インストールも引き続きroot権限で、以下の通りコマンドを実行します。
yum install nginx
インストール完了後、Nginxを自動起動するように設定します。
systemctl enable nginx
最後に起動して、表示を確認しましょう。
systemctl start nginx
以上で起動するはずです。あとはブラウザで「http://サーバーホスト名/」の表示を確認しましょう。
ASP.NET Coreアプリのホスト
ASP.NET Coreアプリをホストするためには、単独のASP.NET Coreアプリが実行できる環境の作成を行います。Windows、Mac、Linuxの開発環境で、あらかじめ実行用ファイルを作成します。
実行用ファイルの作成は、プロジェクトのあるディレクトリに移動し、dotnet CLIを使用して行います。
dotnet publish
publishコマンドの実行に成功するとプロジェクトフォルダー内に「/bin/Debug/netcoreapp2.0/publish」が作成され、発行用モジュール群が作成されます。
publishに作成された実行モジュールが正しく動作するか確認します。
dotnet [作成された実行モジュール名].dll
コマンドラインに表示されたURL(下図にある赤い矢印の箇所)をWebブラウザで表示して、正しく表示が行えれば準備完了です。
Nginxのリバースプロキシー機能を設定
それでは、Nginxのリバースプロキシー機能を設定します。基本的な設定は、Host ASP.NET Core on Linux with Nginxに記載された方法に沿って行います。
ここでは、実際に前述のアプリケーションをホストする手順を紹介します。
アプリ側の設定
今回のホスティングでは、単一のNginxインスタンスと同一のサーバー上にアプリをホストします。
つまり、Nginxからリクエストがフォワードされてくるので、ASP.NET CoreアプリにMicrosoft.AspNetCore.HttpOverrides
パッケージを追加し、パッケージに含まれるForwarded Heaaderミドルウェアを使用します。
このミドルウェアはX-Forwarded-Proto
ヘッダーを使用してRequest.Scheme
を更新することで、リダイレクトURIやその他のセキュリティポリシーが正しく機能することができます。
X-Forwarded-Proto(XFP)
ヘッダーとは、クライアントがプロシキやロードバランサーに接続するために使用したプロトコル(HTTPまたはHTTPS)を識別するためのヘッダーです。
一般的なサーバーのアクセスログには、サーバーとプロキシー間で使用されたプロトコルは残されますが、クライアントとプロキシーとの間のプロトコル情報は含まれません。そのため、クライアントとプロキシー間で使用したプロトコルを識別する場合は、XFPを使用します。
いくつかの種類の認証ミドルウェアでは、Forwarded Headersミドルウェアを最初に適用にすることを必須としています。この適用順序を守ることで認証ミドルウェアがヘッダー値を識別し、正しいリダイレクトURIを生成することができるようになります。
Forwarded Headersミドルウェアは、StartupクラスのConfigureメソッド内で宣言します。
認証ミドルウェアを使用する場合は、UseAuthenticationの前で宣言するようにします。
app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); app.UseAuthentication();
Nginx側の設定
Ngnixは、設定ファイルの定義に基づいてリクエストを処理します。設定ファイルは「/etc/nginx/conf.d」に格納します。(Microsoftサイトでは/etc/nginx/sites-available/defaultと記載がありますが、実際には設定ファイルは「/etc/nginx/conf.d」に格納されています)
Nginxの設定ファイルの記法
設定ファイルの書式は、シンプルディレクティブとブロックディレクティブに分かれます。
シンプルディレクティブの書式は[定義名] [定義値];
で表現します。ブロックディレクティブは、以下の通り複数のディレクティブを内包することができます。
[ディレクティブ] { [ディレクティブ] 定義値; [ディレクティブ] 定義値; [ディレクティブ] { [ディレクティブ] [定義値]; [ディレクティブ] [定義値]; [ディレクティブ] [定義値]; } }
ブロックディレクティブにはevents
、http
、server
、location
などがあり、ブロックディレクティブに内包の定義群のことを「コンテキスト」、そして、このコンテキストの外に置かれたディレクティブ群のことを「メインコンテキスト」と呼びます。
ASP.NET Coreアプリへのリバースプロキシーを行う場合の設定サンプル
ASP.NET Coreアプリへのリバースプロキシーとして設定する場合、以下の通り設定ファイルを記述します。
server { listen 80; server_name example.com *.example.com location / { proxy_pass http://localhost:5000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $http_host; proxy_cache_bypass $http_upgrade; } }
上記の設定では、server_name
ディレクティブには、現在example.com *.example.com
を設定しています。これは、サーバー名にこのアドレスが指定されている場合に、以下の設定に基づくリクエストの転送が行われることを意味しています。
設定ファイルにserver_name
が未指定だった場合は、Nginxはデフォルトサーバーを使用します。デフォルトサーバーが未指定だった場合、設定ファイルに記述される最初のサーバーの定義をデフォルトサーバーと見なします。
そのため、推奨設定としては、デフォルトサーバーが444のステータスコードを返すように設定することで、該当アドレス以外のリクエストを受け付けないように構成することができます。
server { listen 80 default_server return 444; }
逆にすべてのリクエストを処理したい場合はserver_name
行を削除することで、受信したリクエストをすべて処理します。
設定の反映
Nginxの設定の反映は「設定内容の確認」「設定の再読み込み」の順に行います。設定内容の確認はShellから以下のコマンドで行います。
sudo nginx -t
問題がある場合は、問題行の表示が行われます。問題がなければ、設定の再読み込みを行います。設定の再読み込みは、Shellから以下のコマンドで行います。
sudo nginx -s reload
以上でNginxの設定は完了ですが、CentOSはSELinuxが既定でON(Enforcing)になっているため、リクエストが「502 Bad Gateway」となってしまう場合があります。その場合は以下のShellから以下のコマンドを実行して、SELinuxをOFF(Permissive)にすることで動作するようになります。
sudo setenforce 0
Apacheによるホスティング
Apacheは、Linuxの標準的なWebサーバー機能です。
Nginxの特徴が高速・軽量であるのに対して、Apacheは高度な機能を持つWebサーバーで、Linuxにおける利用実績も多数あります。ここではApacheに対するホスティング方法について説明します。なお、Apacheの詳細なインストール方法については、多くのサイトで詳細に紹介されているため、本記事では割愛します。
ここでは参考までに筆者がCentOS 7を使用して、構成した手順を紹介します。前述した手順で、あらかじめ.NET Core SDKのインストールを行っておきます。
Apacheのインストール
最初にCentOSのシステムパッケージの更新を行います。
sudo yum update -y
Apacheのインストールは以下のコマンドで行います。
sudo yum -y install httpd mod_ssl
Apacheをリバースプロキシーとして構成する
Apacheのメインとなる構成ファイルは「/etc/httpd/conf/」ディレクトリにある「httpd.conf」です。
初期インストールされる「httpd.conf」には、IncludeOptional conf.d/*.conf
の記述があるため、「/etc/httpd/conf.d/」ディレクトリにある拡張子が「*.conf」となっているファイルがアルファベット順に読み込まれます。
そこで、「/etc/httpd/conf.d/」ディレクトリに、今回は「c1razor.conf」というファイル名で設定ファイルを作成し、以下の通り記述します。
<VirtualHost *:80> ProxyPreserveHost On ProxyPass / http://127.0.0.1:5000/ ProxyPassReverse / http://127.0.0.1:5000/ ServerName www.example.com ServerAlias *.example.com ErrorLog ${APACHE_LOG_DIR}hellomvc-error.log CustomLog ${APACHE_LOG_DIR}hellomvc-access.log common </VirtualHost>
VirtualHostブロックは複数定義することができるため、1つの「*.conf」ファイルに複数定義することもできます。
前述の通り、設定ファイルは拡張子が「*.conf」であれば読み込まれるため、サイト単位で複数ファイルに分けて定義することもできます。
この構成ファイルは80番ポートのトラフィックを受信し「www.example.com」および「*.example.com」サイトの仮想サーバーとして機能します。
仮想サーバーとして受信したリクエストは、IPアドレスの127.0.0.1、つまり自身の5000ポートに中継し、ProxyPass
とProxyPassReverse
で定義しているため、双方向通信を行います。
まとめ
2回にわたって、ASP.NET CoreやComponentOne for ASP.NET MVCを利用したアプリの開発方法やホスティングについて紹介してきましたが、いかがでしたでしょうか?
ComponentOne for ASP.NET MVCは、非サポートながらもあらゆる環境で高度なコントロールが開発できるため、手間のかかるユーザーインターフェースの開発の大きな手助けになってくれます。
今回はマルチプラットフォームをキーワードに開発方法の違いや、ホスティング方法の違いなどを確認しました。実稼働でホスティングする場合はアプリの死活監視や再起動、他セキュリティなどさまざまな考慮事項があるため、本稿で紹介したリンクなども併せて参考にしてください。
まだまだ、日本語のリソースが少ない状況のASP.NET Coreですが、本稿が何かの参考になれば幸いです。