- สร้างโปรเจ็กส์ Razor
- เพิ่มโมเดล Movie
- Migration
- เปลี่ยนมาใช้ MySQL
- รันดูหน้า Movies
- Seed the database
- Update pages
- Add search
- Add a new field
- Validation
1. สร้างโปรเจ็กส์ Razor
เปิด VS 2019 สร้างโปรเจ็กส์ใหม่แบบ ASP.NET Core Web Application

เลือก Web Application

เพิ่มไฟล์ .gitignore
.vs/ bin/ obj/ *.user artifacts/ .vscode/ .dotnet/ *.binlog
รันจะเจอหน้า Home

และหน้า Privacy

2.เพิ่มโมเดล Movie
สร้างโฟลเดอร์ชื่อ Models
เพิ่มคลาส Movie
using System;
using System.ComponentModel.DataAnnotations;
namespace RazorMovieEFCore.Models
{
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public decimal Price { get; set; }
}
}
สร้างโฟลเดอร์ Pages > Movies
Scaffold the movie model

เลือก Razor Pages using Entity Framework (CRUD)

เลือกโมเดล Movie และเพิ่มคลาส context

3.Migration
ใช้ Package Manager Console (PMC)
PM> Add-Migration Initial PM> Update-Database
ตอนนี้จะได้ดาต้าเบสละ ลองเปิดดูด้วย SQL Server Object Explorer


4.เปลี่ยนมาใช้ MySQL
สามารถเปลี่ยนไปใช้ MySQL ได้ด้วยการติดตั้ง Pomelo.EntityFrameworkCore.MySql
PM> Install-Package Pomelo.EntityFrameworkCore.MySql
ที่ไฟล์ appsettings.json เปลี่ยน ConnectionStrings จาก
"ConnectionStrings": {
"RazorMovieEFCoreContext": "Server=(localdb)\\mssqllocaldb;Database=RazorMovieEFCoreContext-c746480b-4d3e-40f2-abf5-3bd2302f1542;Trusted_Connection=True;MultipleActiveResultSets=true"
}
เป็น
"ConnectionStrings": {
"RazorMovieEFCoreContext": "Server=localhost;User Id=user1;Password=pass1;Database=demo"
}
ที่ไฟล์ Startup.cs ใน.ConfigureServices เปลี่ยนจาก UseSqlServer
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddDbContext<RazorMovieEFCoreContext>(options => options.UseSqlServer(Configuration.GetConnectionString("RazorMovieEFCoreContext")));
}
เป็น UseMySql
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddDbContext<RazorMovieEFCoreContext>(options => options.UseMySql(Configuration.GetConnectionString("RazorMovieEFCoreContext")));
}
ลบไฟล์ในโฟลเดอร์ Migrations ที่ได้จากการทำ Migraion ครั้งก่อน
ทำ Migration ใหม่ (สำหรับ MySQL)
PM> Add-Migration Initial PM> Update-Database
เปิดด้วย phpMyAdmin จะเห็นตาราง movie ละ

ลอง export ออกมาดูจะได้
CREATE TABLE `movie` ( `ID` int(11) NOT NULL, `Title` longtext DEFAULT NULL, `ReleaseDate` datetime(6) NOT NULL, `Genre` longtext DEFAULT NULL, `Price` decimal(65,30) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
5.รันดูหน้า Movies
เรียกหน้า Movies ที่ https://localhost:44333/Movies



6.Seed the database
เพิ่มคลาส SeedData
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using RazorMovieEFCore.Data;
using System;
using System.Linq;
namespace RazorMovieEFCore.Models
{
public static class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
using (var context = new RazorMovieEFCoreContext(
serviceProvider.GetRequiredService<
DbContextOptions<RazorMovieEFCoreContext>>()))
{
// Look for any movies.
if (context.Movie.Any())
{
return; // DB has been seeded
}
context.Movie.AddRange(
new Movie
{
Title = "When Harry Met Sally",
ReleaseDate = DateTime.Parse("1989-2-12"),
Genre = "Romantic Comedy",
Price = 7.99M
},
new Movie
{
Title = "Ghostbusters ",
ReleaseDate = DateTime.Parse("1984-3-13"),
Genre = "Comedy",
Price = 8.99M
},
new Movie
{
Title = "Ghostbusters 2",
ReleaseDate = DateTime.Parse("1986-2-23"),
Genre = "Comedy",
Price = 9.99M
},
new Movie
{
Title = "Rio Bravo",
ReleaseDate = DateTime.Parse("1959-4-15"),
Genre = "Western",
Price = 3.99M
}
);
context.SaveChanges();
}
}
}
}
แก้ Program.cs
public static void Main(string[] args)
{
//CreateHostBuilder(args).Build().Run();
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
SeedData.Initialize(services);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred seeding the DB.");
}
}
host.Run();
}
ลบรายการในตาราง Movie
รันโปรแกรม จะเห็นรายการเพิ่มเข้ามาจากการ Seed

7.Update pages
เปลี่ยนคำว่า ReleaseDate เป็น Release Date

แก้ไข Movie.cs
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorMovieEFCore.Models
{
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
}

แก้ไข link ของ Edit , Detail , Delete จาก
<td> <a href="/Movies/Edit?id=1">Edit</a> | <a href="/Movies/Details?id=1">Details</a> | <a href="/Movies/Delete?id=1">Delete</a> </td>
เป็น
<td> <a href="/Movies/Edit/1">Edit</a> | <a href="/Movies/Details/1">Details</a> | <a href="/Movies/Delete/1">Delete</a> </td>
ด้วยการเปลี่ยนจาก @page เป็น @page "{id:int}" ที่ไฟล์ Edit, Details, Delete ในโฟลเดอร์ Movies (ไฟล์ Index ไม่ต้องแก้)
8.Add search
ทำการ search จาก title
แก้ไขไฟล์ Pages/Movies/Index.cshtml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using RazorMovieEFCore.Data;
using RazorMovieEFCore.Models;
namespace RazorMovieEFCore
{
public class IndexModel : PageModel
{
private readonly RazorMovieEFCore.Data.RazorMovieEFCoreContext _context;
public IndexModel(RazorMovieEFCore.Data.RazorMovieEFCoreContext context)
{
_context = context;
}
public IList<Movie> Movie { get;set; }
[BindProperty(SupportsGet = true)]
public string SearchString { get; set; }
// Requires using Microsoft.AspNetCore.Mvc.Rendering;
public SelectList Genres { get; set; }
[BindProperty(SupportsGet = true)]
public string MovieGenre { get; set; }
public async Task OnGetAsync()
{
//Movie = await _context.Movie.ToListAsync();
var movies = from m in _context.Movie
select m;
if (!string.IsNullOrEmpty(SearchString))
{
movies = movies.Where(s => s.Title.Contains(SearchString));
}
Movie = await movies.ToListAsync();
}
}
}
ลองค้นหาแบบนี้ https://localhost:44333/Movies?searchString=Ghost

เปลี่ยนเป็นค้นหาแบบนี้ https://localhost:44333/Movies/Ghost โดยการแก้ไขไฟล์ Index
@page "{searchString?}"

ใส่กล่อง Search ที่ไฟล์ Pages/Movies/Index.cshtml
@page "{searchString?}"
@model RazorMovieEFCore.IndexModel
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-page="Create">Create New</a>
</p>
<form>
<p>
Title: <input type="text" asp-for="SearchString" />
<input type="submit" value="Filter" />
</p>
</form>
<table class="table">
...

Search by genre
ไฟล์ Pages/Movies/Index.cshtml.cs
public async Task OnGetAsync()
{
// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
orderby m.Genre
select m.Genre;
//Movie = await _context.Movie.ToListAsync();
var movies = from m in _context.Movie
select m;
if (!string.IsNullOrEmpty(SearchString))
{
movies = movies.Where(s => s.Title.Contains(SearchString));
}
if (!string.IsNullOrEmpty(MovieGenre))
{
movies = movies.Where(x => x.Genre == MovieGenre);
}
Genres = new SelectList(await genreQuery.Distinct().ToListAsync());
Movie = await movies.ToListAsync();
}
ไฟล์ Pages/Movies/Index.cshtml
@page "{searchString?}"
@model RazorMovieEFCore.IndexModel
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-page="Create">Create New</a>
</p>
<form>
<p>
<select asp-for="MovieGenre" asp-items="Model.Genres">
<option value="">All</option>
</select>
Title: <input type="text" asp-for="SearchString" />
<input type="submit" value="Filter" />
</p>
</form>
<table class="table">
...

9.Add a new field
ที่ไฟล์ Movie.cs เพิ่ม Rating
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorMovieEFCore.Models
{
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
public string Rating { get; set; }
}
}
เพิ่มคอลัมน์ Rating ที่ Pages/Movies/Index.cshtml
@page "{searchString?}"
@model RazorMovieEFCore.IndexModel
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-page="Create">Create New</a>
</p>
<form>
<p>
<select asp-for="MovieGenre" asp-items="Model.Genres">
<option value="">All</option>
</select>
Title: <input type="text" asp-for="SearchString" />
<input type="submit" value="Filter" />
</p>
</form>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Movie[0].Title)
</th>
<th>
@Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
</th>
<th>
@Html.DisplayNameFor(model => model.Movie[0].Genre)
</th>
<th>
@Html.DisplayNameFor(model => model.Movie[0].Price)
</th>
<th>
@Html.DisplayNameFor(model => model.Movie[0].Rating)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Movie)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.DisplayFor(modelItem => item.Rating)
</td>
<td>
<a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
แก้ไข SeedData.cs (ทุกอันที่ new Movie)
context.Movie.AddRange(
new Movie
{
Title = "When Harry Met Sally",
ReleaseDate = DateTime.Parse("1989-2-12"),
Genre = "Romantic Comedy",
Price = 7.99M,
Rating = "R"
},
อัพเดทดาต้าเบส
PM> Add-Migration Rating PM> Update-Database
ถ้าดูที่ตาราง Movie ตอนนี้จะเห็น Rating มีค่า null ให้ทำการลบข้อมูลในตาราง Movie ให้หมด แล้วค่อยรันโปรแกรมใหม่ เพื่อให้เกิดการ Seed

10.Validation
Update the Movie class to take advantage of the built-in Required, StringLength, RegularExpression, and Range validation attributes.
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorMovieEFCore.Models
{
public class Movie
{
public int ID { get; set; }
[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
//[DisplayFormat(DataFormatString = "{0:dd-MMM-yyyy}", ApplyFormatInEditMode = false)]
public DateTime ReleaseDate { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z""'\s-]*$")]
[Required]
[StringLength(30)]
public string Genre { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
[StringLength(5)]
[Required]
public string Rating { get; set; }
}
}
PM> Add-Migration New_DataAnnotations PM> Update-Database