今日もちょいつか

お酒の飲み過ぎか、それとも歳のせいなのか毎日ちょっぴり疲れ気味なフリーのソフト屋です。ソフト開発の話題をblogにしてみます。

ASP.NET Core MVC 複数ボタンのあるFormでSubmit先を変えたい

いわゆるCRUDではなくて、
一つのフォームで新規登録・修正・削除・一覧表示をしたいと思っています。
名称を登録するだけのような簡単なマスターメンテナンス画面でも
CRUDだとその分Viewが必要になるし、操作もちょっと面倒です。

CRUDだと一つの処理に一つのSubmitですので
Controller側のアクションも簡単なのですが
一つのフォームで全ての処理を行うには複数のSubmitボタンが必要となります。

その時にController側はどのように記述すれば複数のSubmitボタンに対応できるのか
調べてみました。
いつものことですが、今回もかなりハマってしまいました。
Visual Studio Community 2017 for Mac で動作確認しています。

どうしても動かなかった方法

ネットを調べてみると、ある程度同じような方法を発見することができました。
セレクター属性を作ってクリックされたボタンごとにアクションを実行させる方法です。
ボタンのvalue属性を利用して処理を分岐する方法もありましたが
新たにセレクター属性を作ったほうがMVC的な感じがしていいと思いました。

Viewには一つのFormに更新と削除ボタンが二つあります。

<input type="submit" name="btnUpdate" value="更新" class="btn btn-primary">
<input type="submit" name="btnDelete" value="削除" class="btn btn-danger">

属性の作成はこんな感じです。

public class ButtonAttribute : ActionMethodSelectorAttribute
  {
    public string ButtonName { get; set; }

    public override bool IsValidForRequest(ControllerContext context, System.Reflection.MethodInfo methodInfo)
    {
      return context.Controller.ValueProvider.GetValue(ButtonName) != null;
    }
  }

Controller側はこんな感じ。

 [HttpPost]
 [Button(ButtonName = "btnUpdate")]
 public IActionResult Update()
 {
       ViewData["Message"] = "Update.";
       return View("test");
 }

クリックされたボタンのname属性を記述して処理を振り分けています。
すごくわかりやすくて簡単に動いてくれるはずでした。

しかし!
この方法では自分の環境では動いてくれませんでした。
英語サイトも見まくって4,5日奮闘しましたが結局動きません。

最終的に動いた方法

どうしても一つのSubmitで一つのアクションに紐付けたくて
まだ見ていなかった英語サイトを一つ一つ見ていったら思いがけない解決法を発見しました。
解決策はこれです。

<input type="submit" formaction="Update" formmethod="post" value="更新" class="btn btn-primary">
<input type="submit" formaction="Delete" formmethod="post" value="削除" class="btn btn-danger">

Viewでのhtmlの記述を変更するだけで解決することができました。
HTML5になってからボタンに「formaction」と「formmethod」が記述できるようになったのですね。
恐ろしいほどの無知さに悲しくなるのでした。
ものすごく時間をかけて悩んだ挙句こんなオチでした。
でも、解決できてよかった!

ほんのちょっとお小遣い

パソコンで仕事をしていると疲れて飽きてきちゃいます。
そんな時は、簡単にお小遣いを稼ぐのもいいですね。

短時間で高収入のコマーシャルはどれも嘘くさくて。。。
お小遣いサイトなら安全です。
(その代わり収入はちょびっとですが)
ちょびリッチはこちら
メールアドレスのみでノーリスクで利用できます。

ASP.NET Core MVC xUnitに挑戦(かなりの超初心者です)

どうやら、xUnit(テスティングフレームワーク)が
とても便利らしいとの情報をネットで発見したので調べてみました。
テストコードを記述することで、簡単に何度でもテストが
できるのでソースを変更した後の確認がとても楽なのでは!と思いました。

で、ネットを調べてみたのですが・・・。難しい。
ネットのみなさんの技術レベルが高すぎて初心者の私では
とても理解ができないのでした。
それでもなんとかxUnitを動かすことができたのでまとめておきます。

技術レベルがとても低いので、きちんとした作法にのっとっているのかさえ
わからずただ動いたー。だけです。
Visual Studio Community 2017 for Mac で動作確認しています。

テスト対象のプロジェクトを作成する

テスト対象用プロジェクトとして、プロジェクトを作成します。
f:id:Heinlein:20180206133436p:plain
そして「CalcClass.cs」を追加します。引数を加算して結果を返す単純なものです。

namespace xUnitProjct
{
    public class CalcClass
    {
        public int sum(int a, int b){
            int result = a + b;
            return result;
        }
    }
}

そして、HomeController.csはこんな感じ。

     public IActionResult Index()
     {
         CalcClass calc = new CalcClass();
         int subval = calc.sum(1, 2);
         ViewData["CalcValue"] = subval.ToString();
         return View();
     }

テスト用のプロジェクトを作成する

ソリューションを右クリック>追加>新しいプロジェクトを追加。
f:id:Heinlein:20180206101632p:plain
左側の.NET Core>テストを選択してxUnitテストプロジェクトを選択。
f:id:Heinlein:20180206102041p:plain
プロジェクト名は
先程のプロジェクト名 + 「.Tests」として、xUnitProjct.Tests で作成します。

テスト用プロジェクトに参照設定

テスト用に作成した「xUnitProjct.Tests」がテスト対象のプロジェクト「xUnitProjct」を認識できるように参照設定をします。
f:id:Heinlein:20180206103432p:plain
開いた画面の「プロジェクト」タブを選択すると
先程作成したxUnitProjctが現れるのでチェックをしてOKをクリックします。
f:id:Heinlein:20180206134616p:plain

テストコードを記述する

テスト用プロジェクトのUnitTest1.csにテスコトードを記述します。

namespace xUnitProjct.Tests
{
    public class UnitTest1
    {
        [Fact]
        public void Test1()
        {
            CalcClass calc = new CalcClass();
            int actual = calc.sum(1, 2);
            int expect = 3;
            Assert.Equal(expect, actual);
        }
    }
}

期待値と実際の結果が合致するかテストします。
実際のテストの実行は、メニュー > 表示 > テストを選択すると画面右上に
単体テストのタブが現れますので、「全て実行」をクリックするとテストが実行されます。
f:id:Heinlein:20180206143341p:plain
無事にテストが成功となりました。

一応確認のために、わざとテストを失敗させてみます。
期待値のところを expect = 3 から 5 に変更してテストを実行してみます。
f:id:Heinlein:20180206143756p:plain
赤く表示されてテストが失敗となりました。きちんとテストができているようです!

さいごに

なんとかxUnitを動かすことができました。
参考にしたサイトはこちらです。
docs.microsoft.com
参考にしたサイトでは、dotnetコマンドを使って実現していたので
なんとかVisual Studio Community 2017 for Macで実行できるようにがんばりました。

テストの内容としてはとても現実的ではないのでただxUnitが動いたに過ぎません。
ここからいかに現実的で効率の良いテストコードを作成できるかだと思います。

ASP.NET Core MVC CSRF対策を調べてみた

Webシステムの脆弱性CSRF (Cross-Site Request Forgery)
いわゆる、クロスサイトリクエストフォージェリがあります。
その対策として
フォーム側にトークンを埋め込んで、受ける側でトークンを検証することで
自サーバーからのPOSTであることが保証されます。
ASP.NET Core MVCではどのようにするのか調べてみました。
Visual Studio Community 2017 for Mac で動作確認しています。

View側の記述

タグヘルパーでFormを記述します。

<form asp-action="Index" method="post">

すると、Formの中にトークン情報が仕込まれます。
hidden属性で __RequestVerificationToken のvalue
トークン情報がセットされます。

Controller側の記述

POSTを受けるController側にも記述します。

   [HttpPost]
   [ValidateAntiForgeryToken]
   public IActionResult Index(ItemGrpViewModel model)

[ValidateAntiForgeryToken]を記述するだけです。
実際に効いているか確認してみます。
Chromeデベロッパーツールで__RequestVerificationToken のvalueの中身を変更して
Submitしてみます。

f:id:Heinlein:20180201141505p:plain
きちんとエラーではじかれました。

最後にもっと便利な方法がありました

[ValidateAntiForgeryToken]をいちいち全てのPOSTアクションへ記述するのは
面倒だし忘れてしまうかもしれません。
それを一括で可能にする方法がありました!

  [AutoValidateAntiforgeryToken]
  public class HomeController : Controller
  {

Controllerのクラス定義の上に [AutoValidateAntiforgeryToken]を記述することで
全てのPOSTアクションに適用することができます。
もちろんGETアクションにはそのまま利用できます。

このへんはさすがによくできていて、簡単に実装することができます。
もう一から自力で、フルスクラッチで開発しようなどと
とても思わないですね。

ASP.NET Core MVC MySQLのテーブルの内容をDropDownListにしてみた

Webシステムの開発をしていると、テーブルの内容をDropDownListにすることは
よくあることです。マスターデータなんか特にそうですよね。
で、ASP.NET Core MVCのことを色々と考えたり調べたりするのに飽きたので
サクッとコードを書いて、動いたーって喜んでみようと
DropDownListを試してみました。
そーしたら予想外にまたまたハマってしまいました。
まったく、初めてのことはことごとくハマってしまっています。

色々と苦労した結果動いたソース群を記しておきます。
いつものごとく、Visual Studio Community 2017 for Mac で動作確認しています。
自分なりにがんばってみた結果ですので、ASP.NET Core MVCの作法に合っているか不明です。
あくまでも参考程度にしてください。

やりたいことは、
Entity Frameworkを使ってMySQLのテーブルのデータを取得して
DropDownListを表示したい。

DropDownListに表示させたいModel

    public class ItemGrp
    {
        public int Id { get; set; }
        public string ItemGrpName { get; set; }
    }

DropDownList用のViewModelを作成

DropDownListに表示させるためにはViewModelが必要だったのです。
POSTで受け取った時の値も入ります。

    public class ItemGrpViewModel
    {
        [Required]
        public int ItemGrpList { get; set; }
    }

Controllerを準備します

DbContextからItemGrpのidと名称をを取得して、ViewBagへセットします。

        public IActionResult Index()
        {
            ViewBag.SelectOptions = _context.ItemGrp.ToArray().Select(m =>
            new SelectListItem() { Value = m.Id.ToString(), Text = m.ItemGrpName });                                                                      

            return View();
        }

Viewsを準備します

最後にViewへ @Html.DropDownListFor を使って表示します。

    @Html.DisplayNameFor(model => model.ItemGrpList)
    @Html.DropDownListFor(
      model => model.ItemGrpList,
      (IEnumerable<SelectListItem>)ViewBag.SelectOptions,
      " 選択してください ", 
      new { @class = "form-control" } )

最終的にDropDownListに対応するViewModelを準備することで
動いてくれましいた。
ViewをFormタグでくくって、POSTでサーバーへ渡した時に

        [HttpPost]
        public IActionResult Index(ItemGrpViewModel model)

このように、
コンストラクタにViewModelを定義することで値が取得できることもわかりました。
やっぱりViewにはバインドさせるModelが必要なのですね。

仕事の気分転換に、自立式ハンモック

以前からハンモックに憧れていたのですが。
屋内で使える自立式ハンモックがあるのですね。
どんなのが良いのかわからないので、1万円を切るくらいのを買ってみました。
想像していたより気持ちがいいです。
特に左右に揺らすととても気分が癒やされます。
プログラムでハマった時に気持ちを落ち着けることができそうです。

ASP.NET Core MVC MacでMySQLのMigrationが結構ハマりました

Visual Studio Community 2017 for Macを使ってASP.NETの開発をしているのですが
データベース(MySQL)のMigrationを動かすことができずにかなり悩んでしまいました。
最初はなんでもはまってしまうものです。

調べてみると、
Migrationの操作方法は
Package Manager Consoleで操作する方法と
dotnet ef コマンドによる操作があるようです。

このどちらもうまくゆきません。
まず、Package Manager Console ですが
Visual Studio Community 2017 for Macのどこを探しても
発見できないのです。

また、dotnet ef コマンドをTerminalから打ち込んでみるも
「コマンド "dotnet-ef" に一致する実行可能ファイルが見つかりません」
となってしまいます。

とりあえず、Package Manager Consoleの方から
じっくり調べてみました。
すると、なんと!for MacにはPackage Manager Consoleが存在しないようです。
マイクロソフトの公式サイト(英語)にありました。
docs.microsoft.com
日本語に訳して画像にしてみました。
f:id:Heinlein:20180118114234p:plain
MacでのPackage Manager Consoleの利用は無理のようです。

では、dotnet ef コマンドをTerminalから操作するしかありません。
いろいろと調べてみると
dotnet ef コマンドは、

Microsoft.EntityFrameworkCore.Tools.DotNet

で提供されているようです。
どうやらMicrosoft.EntityFrameworkCore.Tools.DotNetをインストールしないといけないようです。
NuGetではインストールできなくて(実際エラーになります)
直接プロジェクトファイルを編集しないといけないようです。
.csprojをエディタで開いて以下を追記します。

  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.1" />
  </ItemGroup>

追加したら、Terminalから

dotnet restore 

の実行で、dotnet ef コマンドが使えるようになりました。

マイグレーションファイルの作成は

dotnet ef migrations add マイグレーション名

マイグレーション実行は

dotnet ef database update

となります。

マイグレーションを実行するとちゃんとMySQLへテーブルが作成されました。
そしてMySQLの中に「__EFMigrationsHistory」テーブルも作成されます。
このテーブルにマイグレーションを実行した履歴が残りますので
次回にマイグレーションを実行する際はこの履歴テーブルに無い
マイグレーションファイルが実行されるわけです。

この仕組を使うと、
ローカルの開発環境からサーバーのデータベース環境への適用ががとても簡単になりますので
積極的に使ってゆきたいと思います。

ASP.NET Core MVC LINQを使ってみる

LINQ (Language Integrated Query) についてまとめてみました。
ASP.NET MVCで開発するには必須の技術となっています。
いままでだと、データベースシステムを開発するときには
SQLを駆使して処理を作成していましたが
ASP.NET MVCではDbContextに格納されたデータに対して
LINQを使って必要なデータを取得するのが作法となっているようです。
新しいことを覚えるのは大変ですが、学ぶのは楽しいことでもあります。
動作は、Visual Studio Community 2017 for Mac で行なっています。

動作確認に利用するデータ

LINQの動作確認に利用するデータ定義です。

var members = new[]{
      new { id = 1, Name = "ゆきこ", Age = 30, SexID = 2},
      new { id = 2, Name = "ようこ", Age = 5 , SexID = 2},
      new { id = 3, Name = "ひろし", Age = 10, SexID = 1}
};
var sex = new[]{
      new { id = 1, Text = "男" },
      new { id = 2, Text = "女"}
};

クエリキーワード[group・by]

指定したキー値にもとづいて、グループ化した結果を返すことができます。

var q =
      from p in members
      orderby p.SexID
      group p by p.SexID;
            
      foreach(var g in q)
      {
          Console.WriteLine("----/" + g.Key.ToString() + "/----");
          foreach (var x in g) Console.WriteLine(x);
      }

実行結果はこうなります。

----/ 1 / ----
{ id = 3, Name = ひろし, Age = 10, SexID = 1 }
----/ 2 / ----
{ id = 1, Name = ゆきこ, Age = 30, SexID = 2 }
{ id = 2, Name = ようこ, Age = 5, SexID = 2 }

同じ処理をメソッド構文で記述してみます。

var m = members
       .OrderBy(x => x.SexID)
       .GroupBy(x => x.SexID);

クエリキーワード[orderby・ascending・descending]

各要素の値にもとづいてクエリ式で返す結果を並べ替えます。

var q =
      from p in members
      orderby p.SexID descending, p.Name
      select p;
      foreach(var x in q)
      {
         Console.WriteLine(x);
      }

この例だと、性別で降順にしてなおかつ名前で昇順にしています。
実行結果は

{ id = 1, Name = ゆきこ, Age = 30, SexID = 2 }
{ id = 2, Name = ようこ, Age = 5, SexID = 2 }
{ id = 3, Name = ひろし, Age = 10, SexID = 1 }

同じ内容をメソッド構文で記述してみました。

var m = members
      .OrderByDescending(x => x.SexID)
      .ThenBy(x => x.Name);

二つ目のソート条件を記述するのがクエリ構文と違って戸惑いました。

クエリキーワード[ join・in・on・equals ]

指定した値が一致している複数のデータソースを結合します。
よくある、コードから名前表示みたいな感じです。

var q =
      from m in members
      join s in sex on m.SexID equals s.id
      select new { m.id, m.Name, Sex = s.Text };
      foreach(var x in q)
      {
          Console.WriteLine(x);
      }

性別コードから性別を表示しています。
実行結果は

{ id = 1, Name = ゆきこ, Sex = 女 }
{ id = 2, Name = ようこ, Sex = 女 }
{ id = 3, Name = ひろし, Sex = 男 }

これを、メソッド構文で記述すると

var query = members.Join(
        sex,
        (x) => x.SexID,
        (y) => y.id, (x, y) => new {
                                     id = x.id, 
                                     Name = x.Name, 
                                     Sex = y.Text
});

恐ろしきラムダ式ですね。
書いている内容が呪文のようで理解ができません。
これは慣れるしかないのですかねー。

最後に

LINQにはクエリ構文とメソッド構文がありますが
メソッド構文で記述するとパット見、何をしているかわからない感じもあるので
クエリ構文がいいかなーと思ってしまいます。
できればどちらかに統一したい思いもありますので
どちらの構文を利用するか悩むところです。

仕事の気分転換に、自立式ハンモック

以前からハンモックに憧れていたのですが。
屋内で使える自立式ハンモックがあるのですね。
どんなのが良いのかわからないので、1万円を切るくらいのを買ってみました。
想像していたより気持ちがいいです。
特に左右に揺らすととても気分が癒やされます。
プログラムでハマった時に気持ちを落ち着けることができそうです。

ASP.NET Core MVC Dapperを使ったMySQLアクセス

前回は、Entiry Framework を使ったMySQLの操作をまとめてみました。
heinlein.hatenablog.com
SQLを直接書かないし、少ないコードでここまでやってくれるのかって感じでしたが
実際にシステムを開発しようとすると、
Entiry Framework の形に合わせなくてはならず
複雑な処理をプログラミングするのにちょっと難しいと感じていました。

そこで、発見したのが「Dapper」です。
直接SQL文を利用することができて自由度が高そうです。
ADO.NET より扱いやすくて、Entiry Frameworkより自由な感じです。
では、MySQLからテーブルの内容を読み込んでみます。
動作は、Visual Studio Community 2017 for Mac で行なっています。

NuGetでパッケージ追加

Dapperを利用するのに必要な「Dapper」パッケージを追加します。
MySQLを扱うので「MySql.Data」も同様に追加します。

データベース環境とModel「ItemGrp.cs」は前回のEntiry Frameworkで使ったものを
そのまま使います。

サンプルコード

適当なControllerにコードを記述します。

using Dapper;
using MySql.Data.MySqlClient;
public IActionResult Index()
{
    string ItemGrpNames = "";
    using (var connection = new MySqlConnection("server=localhost;database=testdb;uid=root;pwd=パスワード"))
    {
        connection.Open();

        var itemGrps = connection.Query<ItemGrp>("SELECT * FROM ItemGrp");

        foreach (var itemGrp in itemGrps)
        {
            ItemGrpNames += " - " + itemGrp.ItemGrpName;
        }
    }
    ViewData["ItemData"] = ItemGrpNames;
    return View();
}

コード自体はすごくわかりやすく記述することができます。
今までプログラミングしていた形に近いのですんなりと書けそうです。
基本的にはEntiry Frameworkを使いますが
Entiry Frameworkでの記述が難しい時にはDapperを使うと
なんとかシステム開発を進めて行けそうです。