- 参考書籍
- コードファーストでDBアクセス
- リクエスト送信→受け取り
- DBでCRUD操作
- コントローラのアレコレ
- モデルクラスでできること色々
- セッション
- 同時実行制御(トランザクション的なの)
- コードファーストで主キーの名前を変える方法
- 入力チェック(バリデーション)
- ルーティング
- Razor
参考書籍
この作者さんの本にも大変お世話になっております。
コードファーストでDBアクセス
(非スキャフォールディング)モデル作成→DB反映→コントローラからDBアクセスまでの流れ
・プロジェクト作成
プロジェクトの作成で「ASP.NET Core Web アプリ(Model-View-Controller)」を選ぼう!
「ASP.NET Core Web アプリ」という似たのがあるので注意!!!
次に
EntityFrameworkCore
EntityFrameworkCore.SqlServer
EntityFrameworkCore.SqlServer.Design(DBファーストの時のみ)
EntityFrameworkCore.Tools
Web.CodeGeneration.Design(スキャルフォールディングの時に要求される)
をNuGetする。
・モデルクラス作成
モデルクラス作る。
ルールは下記。
・クラス名はテーブル名と同名であること
・プロパティは対応するテーブル列と同盟であること
・主キーはId、もしくはクラス名+Id。(AccountくらすならAccountId)
namespace WebApplication2.Models
{
public class Account
{
public int Id { get; set; }
public int age { get; set; }
public string name { get; set; }
public string email { get; set; }
public string password { get; set; }
}
}
・コンテキストクラス作成
コンテキストクラス作る。
モデルクラスをDBに橋渡しする役割。
using Microsoft.EntityFrameworkCore;
namespace WebApplication2.Models
{
//DbContextの継承
public class MyContext:DbContext
{
// コンストラクタ
public MyContext(DbContextOptions<MyContext> options) : base(options) { }
//モデルクラスへのアクセサー
public DbSet<Account> Account { get; set; }
}
}
・DB接続文字列を定義する
appsettings.jsonにて定義。
"ConnectionStrings": { "接続名": "Server=サーバー名;Database=DB名~"}
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MyContext": "Server=(localdb)\mssqllocaldb;Database=WebApplication2(←プロジェクト名);Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
・アプリにコンテキストクラスを登録する
Startup.cs
接続文字列とコンテキストクラスを紐付けする。
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddControllersWithViews();
//コンテキストクラスを登録
services.AddDbContext<MyContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyContext")));
}
・マイグレーションを実行する
マイグレーションはモデルクラスを元にDBを作成・変更する仕組み。
ここでは、DBの作成をしている。
ツール→NuGetパッケージマネージャ→パッケージマネージャコンソール
Add-Migration Initial(マイグレーションを追加)
Update-Database (DBに反映)
※注意
表示→SQL Serverオブジェクトエクスプローラーで確認。
なかなか反映されない時はVisualStudioを再起動。
これ見ると何やってるかなんとなくわかりそう↓
using Microsoft.EntityFrameworkCore.Migrations;
namespace WebApplication2.Migrations
{
public partial class Initial : Migration
{
//upメソッドでテーブル作成
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Account",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
age = table.Column<int>(type: "int", nullable: false),
name = table.Column<string>(type: "nvarchar(max)", nullable: true),
email = table.Column<string>(type: "nvarchar(max)", nullable: true),
password = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Account", x => x.Id);
});
}
//Downメソッドでテーブル削除
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Account");
}
}
}
・コントローラーにコンテキストを注入
using WebApplication5.Models;
namespace WebApplication5.Controllers
{
public class HelloWorldController : Controller
{
private readonly MyContext _context;
//コンストラクタ
//依存性の注入でコンテキストクラスを取得
public HelloWorldController(MyContext context)
{
this._context = context;
}
//アカウント情報をビューに渡す
public IActionResult List()
{
return View(this._context.Account);
}
}
}
・画面の作成
上記コードのListにカーソルを合わせて右クリック
→ビューの追加
→Razorビュー
→ビュー名List・テンプレートEmpty・レイアウトページを使用するにチェック
→追加でこんなんができる↓
List.cshtml
@{
ViewData["Title"] = "List";
}
<h1>List</h1>
↓コードを追加
<!--Accountモデルを宣言-->
@model IEnumerable<WebApplication5.Models.Account>
@{
ViewData["Title"] = "List";
}
<h1>List</h1>
<h2>アカウント情報</h2>
@foreach (var item in Model)
{
<p>@item.Id</p>
<p>@item.age</p>
<p>@item.email</p>
<p>@item.password</p>
}
(有スキャフォールディング)モデル作成→DB反映→コントローラからDBアクセスまでの流れ
モデルクラス作成(非スキャフォールディングと同じ)
↓
スキャフォールディング
Controllersフォルダを右クリック
→追加→コントローラー
→EntityFrameworkを使用したビューがあるMVCコントローラ
→モデルクラス選択&データコンテキストクラスの+ボタンクリック
→ビューの生成・スクリプトライブラリの参照・レイアウトページを使用するにチェック
→追加
・生成ファイル
これでコンテキストクラス作成・DB接続文字列の定義・コンテキストのアプリへの登録も全部やってくれる!
恐ろしい!
↓
最後にマイグレーション。パッケージマネージャコンソールで。
Add-Migration Initial(マイグレーションを追加)
Update-Database (DBに反映)
リクエスト送信→受け取り
cshtmlからリクエスト送信
・リンク
・form
asp-for="Company"にするとidとnameがCompanyになる。
コントローラでリクエスト受け取り
アクションメソッド名を同じにするだけ。
public async Task<IActionResult> Edit(int? id){ 処理 }
public async Task<IActionResult> Detail(int? id){ }
public async Task<IActionResult> Delete(int? id){ }
public async Task<IActionResult> Create(int? id){ }
formからの受け取り
public async Task<IActionResult> Create(Model company){ }
引数名をasp-for(name属性)と同じにすると受け取れる。
DBでCRUD操作
検索形
Select
・結果が複数の場合
// GET: Books/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var book = await _context.Book.FindAsync(id);
if (book == null)
{
return NotFound();
}
return View(book);
}
・結果が1つの場合
// GET: Books/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var book = await _context.Book
.FirstOrDefaultAsync(m => m.Id == id); //複数と1つでここが違う
if (book == null)
{
return NotFound();
}
return View(book);
}
検索条件を複数にする
・FirstOrDefaultAsync(m=>条件式 && 条件式 || 条件式)
条件に合致するデータを1件だけ返す。(複数ヒットしても1件)
mは検索対象のモデル。
FirstOrDefaultAsync(m.Id=>id == id && m.name == name);
更新系
更新系の場合は引数をBind()でモデルバインディングするのがポイント。
オーバーポスティング攻撃対策。
※モデルバインドとは
その名の通り、モデルクラスにバインド(紐付け)すること。
多分もっと広い用途があるので要調査。
Insert文(新規作成)
public async Task<IActionResult> Create([Bind("Id,Title,Price,Publisher,Sample")] Book book)
{
if (ModelState.IsValid)
{
_context.Add(book); // ここでInsert
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
//return RedirectToAction("Index");
}
return View(book);
}
Update文
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,Price,Publisher,Sample,RowVersion")] Book book)
{
中略
try
{
_context.Update(book); //ここでアップデート
await _context.SaveChangesAsync();
}
Delete文
// POST: Books/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var book = await _context.Book.FindAsync(id);
_context.Book.Remove(book); //ここでDELETE
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
コントローラのアレコレ
コントローラーからテンプレートの呼び出す時の挙動
return View();
上記のようにパス指定なしでテンプレートを呼び出すと、以下のようにアクションに対応したテンプレートを呼び出そうとします。
/Views/コントローラー名/アクション名.cshtml
こんな感じで引数指定すれば同コントローラ内の別アクションメソッドを指定できる。
return View("Hoge");
/Views/コントローラー名/Hoge.cshtml
別のコントローラのアクションメソッドを呼びたい時はRedirect
・Redirect(string url)
url:URLをパス指定する。多分Viewsがルートディレクトリ。
・RedirectToPage(string pageName)
pageName:cshtmlのファイル名の拡張子が無い名称。
・RedirectToAction(String actionName [,string controllerName] [,object Values])
-
actionName:アクションメソッド名
-
controllerName:コントローラ名
-
Values:ルートパラメータ
モデルクラスでできること色々
表示名とデータ型と検証属性の指定。データ型は指定する効果がよくわからず。
[DisplayName("値段")] //ビューでの表示名を付与
[DataType(DataType.Currency)] //ビューでのデータ型を指定。
[StringLength(20, ErrorMessage = "{0}は{1}文字以内で入力してください。")] //検証属性
String price {get; set;}
参考
セッション
・参考サイト
Startup.csでセッションの設定をする
public void ConfigureServices(IServiceCollection services)
{
//セッションの設定
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
//セッションを使う
app.UseSession();
}
セッションを使う
// セッションへ値をセット
HttpContext.Session.SetString("key","value");
HttpContext.Session.SetInt32("key2",123);
// セッション変数から値を取得
string mystring = HttpContext.Session.GetString("key");
int myint = HttpContext.Session.GetInt32("key2");
セッションをビューで表示
同時実行制御(トランザクション的なの)
RowVersionというのを使う。
コードファーストで同時実行制御
1,モデルクラスにRowVersion列を追加。
[Timestamp]
public byte RowVersion { get; set; }
2,Add-Migration→Update-Database
3,Edit.cshtmlにRowVersionを追加。
<input type="hidden" asp-for="RowVersion" />
4,Controllerで競合検出
if (ModelState.IsValid)
{
try
{
_context.Update(hoge);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
//ここ↓
ModelState.AddModelError(string.Empty, "競合が検出されました。");
return View(hoge);
}
return ~;
}
DBファーストで同時実行制御
SSMSでrowversionを追加。
ただし、GUIだとデータ型でrowversion型が存在しない。
なのでSQLで打ち込む。
ALTER TABLE tableName ADD
columnName rowversion NULL;
あとの手順はコードファーストとほぼ一緒。Add-Migration→Update-DatabaseはやらなくてOK。
参考サイト
コードファーストで主キーの名前を変える方法
コードファーストだと主キー名が「Id」か「classNameId」しかつけられない。
Companyクラスなら「Id」か「CompanyId」
・参考
・コンテキストクラスを変更する。
public DbSet<Test> Test { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Test>()
.HasKey(b => b.Test_id) //←こっちがモデルクラスの名前
.HasName("Test_id"); // ←こっちがDBのカラム名
}
VisualStudio内のDBで主キー名変更の場合
主キー名を変える場合は一度DBごと消さないとダメ。
手順は
上記の要領でコンテキストクラス変更
→DBを消す
→マイグレーションファイルも消す
→Add-Migration
→Update-Database
入力チェック(バリデーション)
モデルクラスに検証属性を付与。
[StringLength(20, ErrorMessage = "{0}は{1}文字以内で入力してください。")]
public string TeamName { get; set; }
ビューでエラメッセージを表示。
asp-validation-summary="ModelOnly":モデル全体に関わるエラーだけを表示する。
asp-validation-for="CharacterName":CharacterNameに関するエラーを表示。
Hoge.cshtml
<div asp-validation-summary="ModelOnly" class="text-danger"></div> //ModelOnlyはモデル全体に関わるエラーだけを表示する。
<span asp-validation-for="CharacterName" class="text-danger"></span> //CharacterNameに関するエラーを表示。
//入力チェックのためのJavaScriptライブラリのインポート。
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
・参考
ルーティング
pattern:”{controller}/{action}/{id}”
-
{controller}にはコントローラ名
-
{controller=Home}の「=」はこのパラメータの既定値をHomeにする。
-
{action}にはメソッド名(アクションメソッド)
-
{action=Index}の「=」はこのパラメータの既定値をIndexにする。
-
{id}にはメソッドに渡す値
-
/{id?}の「?」はこのパラメータを省略可にする。
Startup.cs
```
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
```
Razor
Razorテンプレートによる変数出力・処理の実行の基本
・サーバー側
ViewBag.変数名=値
ViewData[“変数名”]=値
```
public IActionResult Greet()
{
ViewBag.Message = "こんにちは、世界!";
return View();
}
```
・画面側
@・・・は出力。
@{・・・}はコードブロック。実行。出力するわけではない。
@{
ViewData["Title"] = "Greet";
}
<h1>Greet</h1>
<p>@ViewBag.Message</p>
Razor側でモデルクラスをインポート・取り出し
<!- Bookモデルを参照する></!->
@model IEnumerable<QuickMaster.Models.Book>
// 取り出し
@foreach (var item in Model)
{
<tr>
<td>@item.Title</td>
<td>@item.Price</td>
<td>@item.Publisher</td>
<td>@item.Sample</td>
</tr>
}
ヘルパーメソッド一覧
-
ViewResult View():テンプレートに基づいてページ出力
-
ContentResutl Content(string content):テキスト出力
-
RedirectResult Redirect(string url):指定されたパスに移動(リダイレクト)
-
RedairectToAction() 指定されたアクションに移動(リダイレクト)
-
File():指定されたバイト配列をファイルとして出力
-
NotFound();404エラーを出力
よく使うビューヘルパー/タグヘルパー一覧
ビューヘルパー一覧
-
@foreach
-
@Html.Displayfor データ方に応じて出力を変化させる。
-
@Html.DisplayNamefor プロパティ名↓を表示する。列名のタイトル表示に使う。
タグヘルパー一覧
<form asp-controller="">:コントローラ名に関連づいたフォーム
<form asp-action="">:アクション名に関連づいたフォーム
<a asp-controller="">:コントローラ名に関連づいたリンク
<select asp-for"">:セレクトボックス
<textarea asp-for="">:テキストエリア
<label asp-for="">:ラベル
<span asp-valication-for="">:入力エラー。個別のプロパティ
<div asp-validation-summary="">入威力エラー。モデル全体。
公式:組み込みタグヘルパー一覧↓
動的なSelectボックスの使い方
・参考サイト
サーバ側でセレクトボックスに渡す値を用意。
SelectList()を使う。
--------------------------
SelectList(IEnumerable items, string dataValueField, string dataTextField, object selectedValue)
items:列挙可能なもの。コレクションとかDBの検索結果をそのままぶち込める。
dataValueField:valueに入る値。
dataTextField:画面に表示される値。
selectedValue;初期値。
-------------------------
// 画面にはTeamNameが表示されるが、valueにはTeamIdが入る。
ViewData["TeamId"] = new SelectList(_context.Teams, "TeamId", "TeamName", character.TeamId);
return View(character);
ビューでセレクトボックスを表示。
部分ビュー
Sharedディレクトリに_bubunView.cshtmlを作成。
cshtmlに表示。
<h1>Create</h1>
<partial name="_bubunView" />
<h4>Team</h4>