LINQ

Posted by muchag | C# |
初回投稿:2018-01-23 (火) 22:10:21 | 最終更新:2018-01-25 (木) 22:59:33

Language INtegrated Query(統合言語クエリー)。

【環境】
Visual C#: 15.5.4(2017)
前提

本日勉強を始めたばかりなので、
これから!

とにかく、SQL 専用なわけではない。

LINQ プロバイダー

LINQは、アクセスするデータの種類に応じてLINQ to Objects、LINQ to SQL、LINQ to XML、LINQ to DataSetなどに分類できます

これらはLINQプロバイダーと呼ばれます。
プロバイダーはアクセスするデータソースに応じて動的に切り替わりますので、
アプリケーション開発者がプロバイダーを意識する必要はほとんどありません。

BuildINSIDER:LINQとは?[C#](2014-11-11)

おー よかった。
いっぱい出てきてわけがわからなくなってきていた。

構文

クエリー式とメソッド式がある。

参考サイト

Qiita:LINQチートシート的なもの(2017-12-08)
→ありがたや~。ちゃんと読みましょう!

netplanetes log:BindingSource.DataSource に LINQ to Entity の ObjectQuery を指定してはいけない(2009-10-10)
→まだ意味がよくわからないけど、試行錯誤の途中で、この問題に直面したような気がする。。。
MSDN:コントロールへのオブジェクトのバインド (Entity Framework)

Posted by muchag | C# |
初回投稿:2018-01-23 (火) 22:10:21 | 最終更新:2018-01-25 (木) 22:59:33

LINQ -> Where 条件分岐

Posted by muchag | C# |
初回投稿:2018-01-23 (火) 20:43:44 | 最終更新:2018-01-25 (木) 22:57:02

【環境】
Visual C#: 15.5.4(2017)
経緯

EntityFramwork の実践 をする中で
Where 部の条件分岐をする必要が出た。

その際、参考サイトの手法のまま、メソッド式 LING を利用しており
条件分岐の方法が分からなくて、あれこれ探し回った。

元々

参考サイトの記述は下記の通り。
この記事用に不要部を削除して進行。


var user02 = db.Users.Where((u) => u.Name == "User02").Include("KptBoards").First();

var user02 = db.Users.Where((u) => u.Name == "User02").First();
要件

現状は User02 を取得することになっているけど
条件によって、User02, User03 を分けたい。

前提

実際の稼働確認は、他のコードで行ったので
この記事内のコードの稼働確認は行っていない。

三項演算子

三項演算子による条件分岐は
式形式では成功。
ステートメント形式では失敗。

式形式

成功。 😎


bool flg = true;
var user02 = db.Users.Where((u) => (flg) ? u.Name == "User02" : u.Name == "User03").First();
ステートメント形式

失敗。 😥


bool flg = true;
var user02 = db.Users.Where((u) => { (flg) ? u.Name == "User02" : u.Name == "User03"; }).First();
CS0834 ステートメント本体を含むラムダ式は、式ツリーに変換できません
参考サイト

プログラミング C# – 翔ソフトウェア (Sho’s):[C#][ラムダ式][式木] Expression として扱えるラムダ式と扱えないラムダ式

メソッド
条件分岐なし

まずはメソッドを利用する方法を体験するために
条件分岐なしパターン。


var user02 = db.Users.Where(IsTarget).First();

private bool IsTarget(User u)
{
	return u.Name == "User02";
}

こういう書き方で、引数 u は自動でメソッドへ送られる。

条件分岐あり。そのまんま

上記のまま、条件を足してみた。


bool flg = true;
var user02 = db.Users.Where(IsTarget(flg)).First();

private bool IsTarget(User u, bool flg)
{
	if (flg) return u.Name == "User03";
	
	return u.Name == "User02";
}
CS7036 ‘Main.GetEmployeesWhereLambda(Employee, bool)’ の必要な仮パラメーター ‘boolIncludingQuitted’ に対応する特定の引数がありません。

こういう書き方はできないらしい。

ラムダ式

下記のように記述したら上手くいった。


bool flg = true;
var user02 = db.Users.Where((u) => IsTarget(u, flg)).First();

private bool IsTarget(User u, bool flg)
{
	if (flg) return u.Name == "User03";
	
	return u.Name == "User02";
}

デリゲートも含めて、理屈を理解できていないので
まだまだ挙動を掴めない。

しばらくは勉強ですね。。。

Posted by muchag | C# |
初回投稿:2018-01-23 (火) 20:43:44 | 最終更新:2018-01-25 (木) 22:57:02

ラムダ式

Posted by muchag | C# |
初回投稿:2018-01-23 (火) 18:33:11 | 最終更新:2018-01-25 (木) 22:56:51

話には聞いていた「ラムダ式」。
無名関数のことだったのね~。

MSDN?:ラムダ式 (C# プログラミング ガイド)
Wikipedia:C#の無名関数

【環境】
Visual C#: 15.5.4(2017)
序章

といいつつ、無名関数のことも、まだ良く理解できていない。。。
いつもの如く、習うより慣れろ!で頑張るぞ~!

概念
一言で言うと

ラムダ式とはメソッド定義を式として扱える機能のこと

Qiita:関数型プログラミングって何、ラムダってなんだよ ラムダ式(2016-10-23)

本日、初対面の「ラムダ式」の情報に目を通してきたけど
この一言で、それなりにまとまった気がする。
ありがとうございます。

ザックリ

私なりに解釈すると、今まで1つ1つメソッドを実装してきたけど
ラムダ式なら、わざわざメソッドとして切り出さなくても、式の中に入れ込めるよ!
ってことかしらね~。

三項演算子

直感では「三項演算子」とイメージが似ていると思う。


// if で真面目に書く
if (a) { return b; }
else { return c; }

// 三項演算子を用いる
(a) ? b : c;

// 真面目にメソッドを実装する
string result = GetResult(value);

private string GetResult(string value)
{
	return "result : " + value;
}

// ラムダ式で書く
string result = (value) => "result : " + value;
書式

// 式形式
(引数) => 処理

// ステートメント形式
(引数) => {処理}

// 非同期ラムダ
MSDN?:ラムダ式 (C# プログラミング ガイド) 参照

// 標準クエリ演算子でのラムダ
MSDN?:ラムダ式 (C# プログラミング ガイド) 参照

ルール
式形式

右辺は、式ツリーとなるらしい。


(value) => "result : " + value

右辺の行末 ;(セミコロン)は不要
もちろん、行全体の行末 ;(セミコロン)は必要

ステートメント形式

右辺は、デリゲートとなるらしい。


(value) => { return "result : " + value; }
引数が1つのときは () を省略できる

value => "result : " + value
引数がないときは、() だけ記述する

() => "result : OK"

このときの () は省略できない

引数を複数設定できる

(a, b) => "result : " + a + ", " + b
引数の型を指定できる

ラムダ式では、型推論によって、型を解決できるので
型の指定は不要だけど、指定することも可能。


(string value) => "result : " + value
  • ラムダにはデリゲート型と同じ数のパラメーターが含まれていなければなりません。
  • ラムダに含まれる各入力パラメーターは、対応するデリゲート パラメーターに暗黙的に変換できなければなりません。
  • ラムダの戻り値 (ある場合) は、デリゲートの戻り値の型に暗黙的に変換できなければなりません。

MSDN?:ラムダ式 (C# プログラミング ガイド)

=>

=> を「ラムダ演算子」という。

変数スコープ
  • ラムダ外の変数を参照できる
  • ラムダ内の変数を外部から参照することはできない

ザックリ理解するとこんな感じ?
正確な情報は下記の通り。

  • 取り込まれた変数は、その変数を参照するデリゲートがガベージ コレクションの対象になるまでガベージ コレクトされません。
  • ラムダ式内に導入された変数は、外側のメソッドでは参照できません。
  • ラムダ式は、外側のメソッドの ref パラメーターまたは out パラメーターを直接取り込むことはできません。
  • ラムダ式に含まれる return ステートメントで外側のメソッドを戻すことはありません。
  • ラムダ式には、 goto ステートメント、 break ステートメント、およびジャンプ ステートメントのジャンプ先がブロック外である場合はラムダ式の内部にある continue ステートメントを含めることはできません。 また、ジャンプ先がブロックの内部にある場合は、ラムダ式の外部でジャンプ ステートメントを使用するとエラーになります。

MSDN?:ラムダ式 (C# プログラミング ガイド)

参考サイト

Qiita:はじめての LINQ(2018-01-16)
→LINQ 全体だけど、ひとまずこちらへ。勉強!

Qiita:関数型プログラミングって何、ラムダってなんだよ ラムダ式とスコープ(2016-10-23)

Qiita:C# 今更ですが、ラムダ式(2017-10-27)
→何度も読み返して勉強しましょう!

Qiita:【LINQの前に】ラムダ式?デリゲート?Func?な人へのまとめ【知ってほしい】(2015-02-17)
→何度も読み返して勉強しましょう!

Qiita:C#のExpressionで動的コード生成(2017-12-19)
→式形式(式木、式ツリー)の勉強に!

Qiita:LINQとラムダ式ってなにができるの?(2015-10-04)
→こちらのおかげで、メソッドへ引数を渡す方法に気がついた

@IT:C#ラムダ式 基礎文法最速マスター(2010-03-09)
→ここも分かりやすかった

iPentec:デリゲートとラムダ式の書き換え =>演算子の利用 (C#プログラミング)
→デリゲートと比較しながら勉強するのによさそう

Posted by muchag | C# |
初回投稿:2018-01-23 (火) 18:33:11 | 最終更新:2018-01-25 (木) 22:56:51

DataGridView

Posted by muchag | C#,Visual Studio |
初回投稿:2018-01-23 (火) 14:26:29 | 最終更新:2018-01-23 (火) 17:17:13

データを一覧表示するコントロール。

MSDN:DataGridView コントロール (Windows フォーム)
MSDN:DataGridView クラス

【環境】
Visual C#: 15.5.4(2017)
用途

データを一覧表示するコントロール。

プロパティ

MSDN:DataGridView クラス
一覧はこちらにあるので、逆引きで!

縞々にしたい

MSDN:AlternatingRowsDefaultCellStyle

行ヘッダを消したい

RowHeadersVisible

Posted by muchag | C#,Visual Studio |
初回投稿:2018-01-23 (火) 14:26:29 | 最終更新:2018-01-23 (火) 17:17:13

DbDateTimePicker

Posted by muchag | C#,Visual Studio |
初回投稿:2018-01-23 (火) 14:12:17 | 最終更新:2019-06-10 (月) 10:59:18


@IT:DateTimePickerコントロールでDBNullを扱うには?[C#、VB](2008-01-31)

MSDN:DateTimePicker コントロール (Windows フォーム)
MSDN:DateTimePicker クラス

【環境】
Visual C#: 15.5.4(2017)
経緯
デフォルトの DateTimePicker

DateTimePicker を使いたい。
だけど、null のときに日付が入ってしまうと
空欄にしておきたいユーザが、誤入力をしてしまう。
むぅ。

設計の問題?

Google 先生に聞いてみると、色々とあるみたいだけど
ほとんどの情報が古い。。。

何で改善されないんだろう。
どこかにも書いてあったけど、DateTime 型を利用するのに
null は普通に使うと思うんだけどな~。

私の設計が悪いのかしらね。

記事

それでも、DateTimePicker が null を許容できないことは
当時?みなさんが試行錯誤されたようで、下記のような記事を見つけた。
codeguru:Yet Another Nullable DateTimePicker Control(2005-04-25)
CodeZine:Nullを許容するDateTimePickerコントロール(2005-05-13、日本語訳記事)

null に対応してくれる DateTimePicker

現時点では、よい方針を思いつかないので
DbDateTimePicker を使ってみた。

@IT:DateTimePickerコントロールでDBNullを扱うには?[C#、VB](2008-01-31)
おおお、奇しくも後1週間で10周年w

作成

カスタム コントロールを追加 して
自動生成ソースに、参考サイトのソースを、ちみっと調整してコピペ。


public partial class DbDateTimePicker : DateTimePicker
{
    public DbDateTimePicker()
    {
        InitializeComponent();
    }

    protected override void OnPaint(PaintEventArgs pe)
    {
        base.OnPaint(pe);
    }

    [Bindable(true), Browsable(false)]
    public new object Value
    {
        get
        {
            if (base.Checked)
                return base.Value;
            else
                return DBNull.Value;
        }
        set
        {
            try
            {
                if (Convert.IsDBNull(value))
                {
                    base.Checked = false;
                }
                else
                {
                    base.Value = Convert.ToDateTime(value);
                    base.Checked = true;
                }
            }
            catch (Exception ex)
            {
                base.Value = Convert.ToDateTime(value);
                base.Checked = true;
            }
        }
    }
}
配置

アプリ側にコントロールを追加 して
フォーム上へ配置。
dbdtpDate

コントロールのプロパティから
ShowCheckBox を True

Posted by muchag | C#,Visual Studio |
初回投稿:2018-01-23 (火) 14:12:17 | 最終更新:2019-06-10 (月) 10:59:18

EntityFramwork -> 外部キー

Posted by muchag | C# |
初回投稿:2018-01-22 (月) 20:15:17 | 最終更新:2018-01-26 (金) 12:20:09

【環境】
Visual C#: 15.5.4(2017)
単一
自動

php フレームワークみたいに、
命名規則を守って記述すれば
自動で外部キー設定をしてもらえる。


public class Municipality
{
    [Key]
    [MaxLength(6)]
    public string Code { get; set; }

    [MaxLength(5)]
    public string PrefectureCode { get; set; }

    public Prefecture Prefecture { get; set; } // 追加分
}

こちらは、参考サイトのものを一部頂戴してきたソース。

手動

命名規則からハズレた場合には
手動で外部キーを設定してやる。


public class Municipality
{
    [Key]
    [MaxLength(6)]
    public string Code { get; set; }

    [MaxLength(5)]
    public string PCD { get; set; } // これを変更

    [ForeignKey("PCD")]
    public Prefecture Prefecture { get; set; } // ナビゲーションプロパティ
}

こちらも参考サイトのものを一部頂戴。

Eager-loding, Lazy-loading

おそらく、この先で Eager-loding, Lazy-loading の勉強をする必要があると思う。

複合キー

外部キー
参考スライドの通り


[Key]
[Column(Order=1)]
public string Value1 { get; set; }

[Key]
[Column(Order=2)]
public string Value2 { get; set; }

としたところ、Add-Migration で、下記エラーが出た。

Entity type ‘HogeEntity’ has composite primary key defined with data annotations. To set composite primary key, use fluent API.

そこで、参考サイトのように、Entity でのキー設定をやめ
DbContext 側で設定したところ、設定できた。


protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity()
        .HasKey(paidHoliday => new { HogeEntity.Value1, HogeEntity.Value2 });
}
参考サイト

Qiita:[EntityFrameworkCore]CodeFirstでテーブルに複合主キーを付ける(2017-08-31)
→参考スライドの [Column(Order=1)] では設定できなかったので、大変助かりました

利用

上記のように設定しておけば、Join しなくても
オブジェクトチェーンで取得可能。
Municipality.Code
Municipality.Prefecture.Code

参考サイト

インクリメンタルなカイハツにっき
実践 Entity Framework ~ 外部キーを設定する(2015-07-28)
実践 Entity Framework ~ ForeignKey 属性(2015-08-03)

Posted by muchag | C# |
初回投稿:2018-01-22 (月) 20:15:17 | 最終更新:2018-01-26 (金) 12:20:09

EntityFramwork -> 実践

Posted by muchag | C#,Visual Studio |
初回投稿:2018-01-21 (日) 20:14:57 | 最終更新:2018-01-25 (木) 22:58:45

お試し で大分理解できたので
実際に挑戦!

【環境】
Visual C#: 15.5.4(2017)
プロジェクト作成

お試し との変更点は
メインをフォームアプリにしたこと。

参照設定

フォームアプリプロジェクトへ、NuGet パッケージ管理から、下記をインストール。

  • SQLitePCLRaw.bundle_e_sqlite3
  • SQLitePCLRaw.bundle_sqlcipher
  • SQLitePCLRaw.bundle_green
スタートアッププロジェクト

上記3つのプロジェクトについて、何をするかによって
スタートアッププロジェクトを切り替えないければならない。

  • フォームプロジェクト
    • フォームアプリを動かすときは、これがスタートアッププロジェクト
  • ビジネスロジックプロジェクト
    • マイグレーションをするときは、これがスタートアッププロジェクト
  • ビジネスロジックテストプロジェクト
    • テストをするときは、これがスタートアッププロジェクト
初回マイグレーション
エラー1

Add-Migration Initial
More than one DbContext was found. Specify which one to use. Use the '-Context' parameter for PowerShell commands and the '--context' parameter for dotnet commands.
原因

DbContext クラスが複数あったため。

解決

対象 DbContext クラスを指定してコマンド実行。


Add-Migration Initial -Context MyAppSystem.EmployeeModel
To undo this action, use Remove-Migration.
Update-Database -Context MyAppSystem.EmployeeModel
Applying migration '20180121111141_Initial'.
Done.
エラー2

Add-Migration Initial -Context MyAppSystem.EmployeeModel
Could not load assembly 'MyApp'. Ensure it is referenced by the startup project 'MyAppSystem'.
原因

パッケージ マネージャー コンソール上部にある
「既定のプロジェクト」が、MyApp になっていた。

解決

「既定のプロジェクト」を、MyAppSystem に変更。

フォームイベント

今回は、フォームロード時に走らせる処理として Main_Load メソッドとした。
Main の部分は、フォームクラス名を入れるのが通例らしい。

フォームプロパティのイベント一覧(雷マーク)において
Load => Main_Load
と設定するだけ。

射影

Entity の中から、特定のプロパティを取得する=表示カラムを絞る。
このことを「射影」というらしい。


var arrayEmployees = db.Employees
    .Select(record => new {
        record.EmployeeCode,
        Name = record.FamilyName + " " + record.FirstName
    })
    .ToArray();
  • record へ新しい配列をセットするよ~
  • 配列のプロパティは、record.EmployeeCode と Name だよ~
  • record.EmployeeCode はそのままでいいけど、Name は、record の FamilyName と FirstName を半角スペースで連結して戻してね~

という風に、計算や加工も含めて自由に取得できる。

record

この部分の表記方法を勉強中。

LINQ の考え方としては「配列の要素」を指していると思う。

foreach (var value in array) の value に当たる感じ?

今回は、DB から抽出したレコード群が要素なので
record としてみたけど、何でもよさそう。

Count()

データの個数。


db.Employees.Count()
カラム名指定

上記例に合わせると、こうなる。


dgvEmployees.Columns[nameof(Employee.EmployeeCode)].HeaderText = "従業員コード";
dgvEmployees.Columns[nameof(Name)].HeaderText = "氏名";

ここで面白いのが
Employee.EmployeeCode を EmployeeCode とするとエラー。
record.EmployeeCode としてもエラー。
Name は、何も付けなくてもエラーにならないどころか、想定通りに動く。

まだまだ意味がわかっていない。

参考サイト

Qiita:Entity Framework6で単純なデータ表示フォームを作成(2015-09-08)
→DataGridView へのデータの登録の仕方。DataGridView のカラム名の変更の仕方

Keibalight プログラミングを淡々と:【LINQ】要素の個数を取得するには(Count集計演算子)(2011-07-19)
→LINQ による、データ個数の数え方

Posted by muchag | C#,Visual Studio |
初回投稿:2018-01-21 (日) 20:14:57 | 最終更新:2018-01-25 (木) 22:58:45

EntityFramwork -> お試し

Posted by muchag | C#,Visual Studio |
初回投稿:2018-01-20 (土) 15:57:34 | 最終更新:2018-01-25 (木) 22:58:33

今ひとつピンとこないので
勉強のため、テストプロジェクトを作成。

【環境】
Visual C#: 15.5.4(2017)
経緯

EntityFramework なるものを見つけて
ぜひ挑戦したいと思った。

ただ、慣れない VS 環境であることもあり
なかなかピンとこない。

そこで、参考サイトをそのまま実践してみることにした。

SlideShare:「Entity Framework Coreを使ってみる」 公開用(2017-03-07)

プロジェクト作成
Model プロジェクト
[ファイル]-[新規作成]-[プロジェクト] 「新しいプロジェクト」
左ペイン [インストール済み]-[Visual C#]-[Windows クラシック デスクトップ] 右ペイン [クラス ライブラリ(.NET Framework)] 名前:KptBoardSystem
場所:任意
ソリューション名:「名前」から自動入力
[ソリューションのディレクトリを作成] にチェック
[新しい Git リポジトリの作成] にチェックなし
[OK]
参照設定

プロジェクトコンテキストメニューから [NuGet パッケージの管理] Microsoft.EntityframeworkCore で検索
Microsoft.EntityframeworkCore.Sqlite 2.0.1
Microsoft.EntityframeworkCore.Tools 2.0.1

テストプロジェクト

ソリューションのコンテキストメニューから [追加]-[新しいプロジェクト] 後は前述同様に KptBoardSystemTest プロジェクトを作成。

参照設定

前述同様に、下記を設定。
Microsoft.EntityframeworkCore 2.0.1
xunit 2.3.1
xunit.runner.visualstudio 2.3.1
Moq 4.8.1

初回マイグレーション
クラス作成

P.12 User
P.13 KptBoardModel

コマンド

Enable-Migrations
Enable-Migrations is obsolete. Use Add-Migration to start using Migrations.
Add-Migration setp00
To undo this action, use Remove-Migration.
  1. マイグレーションを可能にする
  2. マイグレーションを追加する
ファイル生成

こんな感じで2ファイルが自動生成された。


KptBoardSystem
┗ Migrations
  ┣ 20180120074120_setp00.cs
  ┗ KptBoardModelModelSnapshot.cs
データベース生成
コマンド

Update-Database
Applying migration '20180120074120_setp00'.
Done.

参考サイト P.15 のように処理がズラズラ並ぶのではなく
とても簡素なリアクションでしたw

ファイルの場所

参考サイトには「ソリューション直下に「kpt.db」が作成される」とあったけど
実際には、ソリューションの2つ上に作成された。

後述の試行錯誤に見られた、テストとの階層の差分があるということは
コマンドを打つ際に、階層を設定するべきなのではないかと思うけど
現在は手法不明。

パス変更

そこで、

KptBoardModel.cs

のパスを下記のように変更したところ、ソリューション直下に作成された。


- var connectionString = new SqliteConnectionStringBuilder { DataSource = "../../../kpt.db" }.ToString();
+ var connectionString = new SqliteConnectionStringBuilder { DataSource = "../kpt.db" }.ToString();

この件には、大きな落とし穴が。。。
詳細は、後述の「試行錯誤」コーナーにて。

注意

コマンドを打つ際に、パッケージ マネージャー コンソールにおいて
「既定のプロジェクト」で何を選択しているかでファイル出力場所が変わる。

“kpt.db” というパスの場合は
プロジェクト直下へ生成されるので
KptBoardSystem を選択していれば、

KptBoardSystem/KptBoardSystem/kpt.db

KptBoardSystemTest を選択していれば、

KptBoardSystem/KptBoardSystemTest/kpt.db

となる。

KptBoardSystem/KptBoardSystem/kpt.db

この場合、テストではファイルが見つからないエラーになってしまうので
「既定のプロジェクト」は注意。

配置

後述の如く、コマンドと実行でファイルパスの意味合いが変わるので
コマンドで生成された DB ファイルを手動で配置換えをした。

テストと本番で DB を分けることも考え
ソリューション直下ではなく、プロジェクト直下としてみる。

つまり、最終的な配置とファイルパスの指定は下記の通り。

KptBoardSystem/KptBoardSystemTest/kpt.db

var connectionString = new SqliteConnectionStringBuilder { DataSource = "../../kpt.db" }.ToString();
注意

このようなやり方にすることで
コマンドを打つ度に
ファイルの移動が必要。

テスト
参照設定

P.18
KptBoardSystemTest プロジェクト内「参照」のコンテキストメニュー [参照の追加] 「参照マネージャー」
左ペイン [プロジェクト]-[ソリューション] 中央ペイン [KptBoardSystem] にチェック
[OK]

クラス作成

P.19 KptBoardModelTest


using Xunit;

namespace KptBoardSystem.Test
{
    public class KptBoardModelTest
    {
        [Fact]
        public void TestInsertUser()
        {
            using (var db = new KptBoardModel())
            {
                var user = new User
                {
                    Name = "User01"
                };
                db.Add(user);

                db.SaveChanges();
            }
        }
    }
}
テスト エクスプローラー
[テスト]-[ウィンドウ]-[テスト エクスプローラー] で、開くことができる。

実施
[テスト]-[実行]-[全てのテスト] または、テスト エクスプローラーにて、対象テストを選択した上で
[テスト]-[実行]-[選択したテスト] で、テストを開始。

P.38
例外

私の記述ミスのせいで、


var kptBoard = db.KptBoards.Where(b => b.Problem.Contains("続ける")).First();

こちらが空になってしまった。

その際、下記のエラーが出た。

[2018/01/21 12:28:10 Error] [xUnit.net 00:00:01.7574752] KptBoardを修正する [FAIL] [2018/01/21 12:28:10 Informational] [xUnit.net 00:00:01.7585683] System.InvalidOperationException : シーケンスに要素が含まれていません
テストのデバッグ
[テスト]-[デバッグ] から、テストのデバッグをできる。

まだ使い方はよくわからない。

LINQ

P.34 では、u が括弧書きだったのに
今回は括弧がつかない理由がわからない。


var user02 = db.Users.Where((u) => u.Name == "User02").Include("KptBoards").First();

var kptBoard = db.KptBoards.Where(b => b.Problem.Contains("続ける")).First();

LINQ を勉強すべし。

試行錯誤
テスト実施1
エラー

テスト初回実行時、下記のエラーが出た。

[2018/01/20 17:48:52 Error] [xUnit.net 00:00:01.4263057] KptBoardSystem.Test.KptBoardModelTest.TestInsertUser [FAIL] [2018/01/20 17:48:52 Informational] [xUnit.net 00:00:01.4272062] System.Exception : You need to call SQLitePCL.raw.SetProvider(). If you are using a bundle package, this is done by calling SQLitePCL.Batteries.Init().
原因

Google 先生に質問したところ、どうやら下記パッケージが不足していることらしい。

解決

テストプロジェクトへインストール。


Install-Package SQLitePCLRaw.bundle_e_sqlite3 -Version 1.1.9
Install-Package SQLitePCLRaw.bundle_sqlcipher -Version 1.1.9
Install-Package SQLitePCL.bundle_green -Version 0.9.3

NuGet パッケージ管理からインストールしても OK だと思う。

参考サイト

stackoverflow:Cannot run SQLite.net PCL in ASP.NET MVC(2017-02-09)

テスト実施2
エラー

上記エラーが取れてホッとしつつテストを行うと
次は下記エラーが出た。

[2018/01/20 21:48:11 Error] [xUnit.net 00:00:01.5294057] KptBoardSystem.Test.KptBoardModelTest.TestInsertUser [FAIL] [2018/01/20 21:48:11 Informational] [xUnit.net 00:00:01.5303380] Microsoft.EntityFrameworkCore.DbUpdateException : An error occurred while updating the entries. See the inner exception for details.
[2018/01/20 21:48:11 Informational] [xUnit.net 00:00:01.5304014] —- Microsoft.Data.Sqlite.SqliteException : SQLite Error 1: ‘no such table: Users’.

Users テーブルが見つからないってさ。。。

調査
DB

kpt.db の中には、確かに Users テーブルがある。

ファイルパス

上で、DB ファイルへのパスを変えたからかもしれないと思い
パスを戻すと、エラーが出ないときもあるけど、確実ではないし
値は書き込まれない。

DB 接続

そもそも DB 接続に失敗しているのかと思って
ファイルパスを存在しないパスにしてみたら
エラー内容が下記の通り変わったので、接続には成功しているみたい。

[2018/01/20 21:52:21 Error] [xUnit.net 00:00:01.3878517] KptBoardSystem.Test.KptBoardModelTest.TestInsertUser [FAIL] [2018/01/20 21:52:21 Informational] [xUnit.net 00:00:01.3888267] Microsoft.Data.Sqlite.SqliteException : SQLite Error 14: ‘unable to open database file’.
ファイル実体

途方に暮れて、ソリューションディレクトリをあちこち見ていたら
想定外の場所に kpt.db ファイルが!!!

原因

コマンドで作成した kpt.db のファイルパスと
テストコードが参照する kpt.db のファイルパスが
異なることが原因だった。

と言っても、何かがおかしいので
いずれわかる日が来ると信じたい。

ともかく、下記のような検証結果だった。


GrandMother
┣ Mother
┃ ┣ KptBoardSystem
┃ ┃ ┣ KptBoardSystem
┃ ┃ ┣ KptBoardSystemTest
┃ ┃ ┃ ┣ bin
┃ ┃ ┃ ┃ ┣ Debug
┃ ┃ ┃ ┃ ┃ ┗ kpt.db // テスト "kpt.db"
┃ ┃ ┃ ┃ ┣ Release
┃ ┃ ┃ ┃ ┗ kpt.db // テスト "../kpt.db"
┃ ┃ ┃ ┗ kpt.db // コマンド "kpt.db"、テスト "../../kpt.db"
┃ ┃ ┗ kpt.db // コマンド "../kpt.db"、テスト "../../../kpt.db"
┃ ┗ kpt.db // コマンド "../../kpt.db"
┗ kpt.db // コマンド "../../../kpt.db"

つまり
コマンドは、プロジェクトディレクトリがルート。
テスト(実行)は、アセンブリ(.dll)本体のある場所がルート?

Posted by muchag | C#,Visual Studio |
初回投稿:2018-01-20 (土) 15:57:34 | 最終更新:2018-01-25 (木) 22:58:33
« 前ページへ