- สร้างโปรเจ็กส์ 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