Teknologi WarisanPeringatan: baik ASP.NET MVC sudah usang dan ADO.NET juga. Dianjurkan untuk menggunakan ASP.NET Core dengan ORM modern. Tetapi jika Anda tertarik, maka bacalah.
Sudah, mungkin, tiga kali saya sampai di ASP.NET MVC. Setelah sepuluh tahun dengan ASP.NET WebForms, agak sulit untuk beralih ke teknologi MVC, karena ada begitu banyak perbedaan sehingga agak lebih mudah untuk membuat daftar apa kesamaan teknologi ini - kecuali perpustakaan .NET Framework. Saya tidak akan menulis di sini - MVC lebih baik atau lebih buruk daripada WebForms, mereka berdua bagus, dan Anda dapat membangun aplikasi yang baik pada kedua teknologi. Saya juga meninggalkan pikiran saya tentang perlunya TDD dengan saya, meskipun saya memilikinya.
Dan sekarang saya akan berbicara tentang tugas yang paling standar - pekerjaan biasa dengan data: melihat daftar catatan dalam bentuk tabel, menambah, mengubah dan menghapus data (operasi CRUD). Namun, di hampir semua buku dan banyak solusi Internet untuk ASP.NET MVC, untuk beberapa alasan, opsi ini secara eksklusif dipertimbangkan melalui ORM (Object Relation Mapping): Entity Framework (EF) atau LINQ untuk SQL. Teknologi sangat baik, tidak diragukan lagi, akhirnya, programmer mungkin tidak mengerti - tetapi bagaimana DBMS yang sangat relasional ini (yang kemungkinan besar ia gunakan) umumnya bekerja, dan bahkan SQL, secara teori, tidak lagi diperlukan: meletakkan dalam bentuk EF dan konektor untuk DBMS akan saling memahami. "Ini dia kebahagiaan - tidak ada lagi keindahan untuk itu." Tetapi bagi para programmer yang tidak takut bekerja secara langsung dengan database melalui mekanisme ADO.NET, seringkali tidak dapat dipahami - dan di mana untuk memulai dengan ASP.NET MVC secara umum dan apakah perlu.
Plus, bagi saya, misalnya, pada awalnya menyebabkan kerusakan liar pada kurangnya komponen yang nyaman untuk menampilkan data dalam tabel kisi. Dapat dipahami bahwa pengembang sendiri harus mengimplementasikan semua ini atau mengambil sesuatu yang cocok dari manajer paket. Jika Anda, seperti saya, sangat senang dengan komponen GridView di ASP.NET WebForms, maka untuk MVC sulit untuk menemukan sesuatu yang lebih atau kurang sebanding, kecuali untuk Grid.mvc. Tetapi kepercayaan saya pada komponen seperti itu tidak cukup untuk menggunakannya untuk proyek yang cukup besar. Jika mereka digunakan, programmer mulai bergantung pada pengembang lain yang, pertama, tidak tahu bagaimana menulis komponen ini (apakah kualitatif?), Dan kedua, tidak diketahui kapan dan bagaimana akan diselesaikan. Tampaknya kadang-kadang bahkan mungkin untuk memperluas komponen dan melihat lebih jauh, tetapi, jika pengembang menyelesaikannya, kami terpaksa menggali kembali kode kami lagi atau membekukan pembaruan komponen pada versi tertentu. Dan jika pengembang memperbaiki kerentanan, Anda masih harus memutakhirkan ke versi yang baru. Bahkan transisi ke versi baru dari konektor MySQL menyebabkan masalah tertentu, meskipun masih dikembangkan oleh perusahaan besar, tetapi bagaimana dengan banyak "sepeda" di manajer paket Nuget?
Jadi, mari kita coba belajar menulis di bawah ASP.NET MVC, sambil bekerja dengan data yang diproses oleh DBMS MySQL. Semua kode dapat diambil di sini di
alamat ini , di bawah kode ini akan disajikan sebagian dengan tautan kecil dan penjelasan. Dan di
sini Anda dapat melihat aplikasinya.
Buat database di MySQL
CREATE SCHEMA `example`; USE `example`; CREATE TABLE `users` ( `UserID` int(4) unsigned NOT NULL AUTO_INCREMENT, `SupporterTier` enum('None','Silver','Gold','Platinum') NOT NULL DEFAULT 'None', `Loginname` varchar(255) NOT NULL, `LanguageID` int(4) unsigned NOT NULL DEFAULT '2', `Email` varchar(255) DEFAULT NULL, `LastLoginDate` datetime DEFAULT NULL, PRIMARY KEY (`UserID`), KEY `i_Loginname` (`Loginname`), KEY `i_Language_idx` (`LanguageID`), CONSTRAINT `i_Language` FOREIGN KEY (`LanguageID`) REFERENCES `languages` (`LanguageID`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; CREATE TABLE `languages` ( `LanguageID` int(4) unsigned NOT NULL AUTO_INCREMENT, `LanguageName` varchar(50) NOT NULL DEFAULT '', `Culture` varchar(10) DEFAULT NULL, PRIMARY KEY (`LanguageID`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Basis data (DB) akan sederhana dan diwakili oleh hanya dua tabel, dengan satu hubungan satu-ke-banyak di antara mereka di bidang
LanguageID
. Dengan cara ini saya akan memperumit situasi untuk kebutuhan untuk menggunakan daftar drop-down dari salah satu bahasa untuk pengguna. Selain itu, untuk komplikasi, untuk pengguna, kami juga akan memperkenalkan bidang
SupporterTier
, yang akan menentukan tingkat dukungan pengguna melalui enum. Dan untuk menggunakan hampir semua tipe data dalam proyek kami, kami menambahkan bidang
LastLoginDate
dari tipe "tanggal / waktu", yang akan diisi oleh aplikasi itu sendiri ketika pengguna masuk (tidak tercermin dalam proyek ini).
Buat proyek

Pilih "MVC". Dimungkinkan untuk menggunakan "Kosong", tetapi kami memiliki pelatihan, bukan aplikasi nyata, jadi ini akan membantu kami segera, tanpa gerakan yang tidak perlu, mengintegrasikan Bootstrap dan JQuery ke dalam aplikasi kami.

Kami sudah mengisi
file Content ,
Fonts ,
Scripts folder, serta
BundleConfig.cs dan
FilterConfig.cs di direktori
App_Start dengan bundel dan filter pendaftaran ASP.NET MVC. Dalam proyek kosong, hanya ada pendaftaran rute di file
RouteConfig.cs . Di
Global.asax.cs, panggilan metode yang dijelaskan dalam file ini juga akan ditambahkan:
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles);
Kami menyiapkan infrastruktur dan seluruh bundel
Untuk bekerja dengan DBMS MySQL, tambahkan perpustakaan
MySql.Data : baik secara manual, jika
konektor mysql-connector-net-8.0.18 sudah diinstal pada komputer, atau dari manajer paket Nuget:

Tambahkan konfigurasi string koneksi ke DBMS MySQL ke file
Web.config :
<connectionStrings> <add name="example" providerName="MySql.Data.MySqlClient" connectionString="server=localhost;Port=3306;user id=develop;Password=develop;persistsecurityinfo=True;database=example;CharSet=utf8;SslMode=none" /> </connectionStrings>
Tambahkan baris ke bagian
<appSettings>
dengan tautan ke string koneksi yang ditambahkan:
<add key="ConnectionString" value="example" />
Kami menambahkan direktori
Domain baru ke aplikasi, di dalamnya kami membuat kelas
Base
statis baru (dalam file
Base.cs ), di mana kami mengakses parameter ini:
public static class Base { private static string ConnectionString { get { return System.Configuration.ConfigurationManager.AppSettings["ConnectionString"]; } } public static string strConnect { get { return System.Configuration.ConfigurationManager.ConnectionStrings[ConnectionString].ConnectionString; } } }
Saya suka memiliki kelas dasar tertentu dalam aplikasi dengan tautan ke parameter aplikasi dan beberapa fungsi standar yang dapat dipanggil dari seluruh aplikasi.
Nama string koneksi didefinisikan dalam pengaturan aplikasi, sehingga di masa depan akan lebih mudah untuk bekerja dengan string koneksi ke DBMS: dengan cepat beralih antara database yang berbeda dan ubah pengaturan koneksi. Selain itu, mudah untuk menggunakan nama string koneksi dalam parameter aplikasi untuk menerbitkan aplikasi ke Microsoft Azure - di sana Anda dapat mengatur parameter untuk layanan aplikasi yang digunakan untuk penerbitan, dan di dalamnya menentukan string koneksi yang diinginkan, yang ditentukan sebelumnya dalam
<connectionStrings>
. Kemudian, saat menerbitkan, Anda tidak dapat menggunakan transformasi
file web.config .
Saya juga suka menggunakan nilai-nilai dari file sumber daya global dalam teks, agar tidak menimpa mereka di beberapa tempat, jika Anda tiba-tiba membutuhkannya. Sebagai contoh:

Dalam file tata letak halaman
_Layout.cshtml (secara standar terletak di direktori
Views \ Shared dan akan digunakan nanti untuk semua halaman proyek ini), Anda sekarang dapat menggunakan variabel-variabel ini (lihat, misalnya,
Example_Users.Properties.Resources.Title
):
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>@ViewBag.Title – @Example_Users.Properties.Resources.Title</title> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") </head> <body> <div class="container body-content"> <h1 class="page-header"><a href="/">@Example_Users.Properties.Resources.Title</a></h1> @RenderBody() <hr /> <footer> <p> @DateTime.Now.Year – @Example_Users.Properties.Resources.Author</p> </footer> </div> @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/bootstrap") @RenderSection("scripts", required: false) </body> </html>
Juga dalam file ini kita melihat lampiran dari style cascading style dari Bootstrap yang terhubung dan pustaka skrip JQuery. Isi dari semua tampilan akan dihasilkan di lokasi panggilan ke fungsi
RenderBody()
.
M berarti model
Tambahkan file
UserClass.cs ke direktori
Domain :
[DisplayName("User")] public class UserClass { [Key] [HiddenInput(DisplayValue=false)] public int UserID { get; set; } [Required(ErrorMessage="Please enter a login name")] [Display(Name = "Login")] public string Loginname { get; set; } public virtual LanguageClass Language { get; set; } [EmailAddress(ErrorMessage = "Please enter a valid email")] public string Email { get; set; } [UIHint("Enum")] [EnumDataType(typeof(Supporter))] [Required(ErrorMessage = "Please select supporter tier")] [Display(Name = "Supporter")] public Supporter SupporterTier { get; set; } [HiddenInput(DisplayValue = true)] [ScaffoldColumn(false)] [Display(Name = "Last login")] public DateTime? LastLoginDate { get; set; } [HiddenInput(DisplayValue = false)] public bool IsLastLogin { get { return LastLoginDate != null && LastLoginDate > DateTime.MinValue; } } public UserClass() {} public UserClass(int UserID, string Loginname, LanguageClass Language, string Email, DateTime? LastLoginDate, Supporter SupporterTier) { this.UserID = UserID; this.Loginname = Loginname; this.Language = Language; this.Email = Email; this.LastLoginDate = LastLoginDate; this.SupporterTier = SupporterTier; } } public enum Supporter { [Display(Name="")] None = 1, Silver = 2, Gold = 3, Platinum = 4 }
Dan juga file
LanguageClass.cs di direktori yang sama:
[DisplayName("Language")] public class LanguageClass { [Key] [HiddenInput(DisplayValue = false)] [Required(ErrorMessage = "Please select a language")] [Range(1, int.MaxValue, ErrorMessage = "Please select a language")] public int LanguageID { get; set; } [Display(Name = "Language")] public string LanguageName { get; set; } public LanguageClass() {} public LanguageClass(int LanguageID, string LanguageName) { this.LanguageID = LanguageID; this.LanguageName = LanguageName; } }
Di sini Anda dapat melihat bahwa properti dari kelas mengulangi struktur tabel
Users
and
Languages
di DBMS.
enum Supporter
dibuat untuk tipe enumerasi sehingga dapat digunakan untuk properti kelas
SupporterTier
bidang serupa di tabel database. Untuk bidang
UserID
,
LanguageID
, Anda bisa melihat bahwa itu ditentukan sebagai kunci utama, seperti dalam database. Atribut
[Key]
digunakan untuk ini.
Semua atribut lainnya lebih cenderung terkait dengan tampilan menggunakan kelas ini. Dan jika kita akan menggunakan helper untuk membentuk tag HTML untuk properti ini (yang secara pribadi saya sarankan), maka kita harus mengatur atribut ini dengan sangat hati-hati untuk mendapatkan yang kita butuhkan. Secara khusus, inilah yang diperlukan untuk proyek ini:
[DisplayName]
- Digunakan sebagai nama tampilan untuk kelas. Kadang-kadang bisa bermanfaat, dalam proyek ini saya secara khusus menambahkan penggunaan helper Html.DisplayNameForModel
untuk demonstrasi.[Display]
dengan properti Name
- digunakan sebagai nama properti kelas yang ditampilkan di layar. Ada juga properti Order berguna yang memungkinkan Anda untuk memesan urutan menampilkan properti kelas dalam formulir menggunakan pembantu (secara default, mengurutkan berdasarkan urutan di mana properti kelas didefinisikan, sehingga properti Order tidak digunakan dalam proyek ini).[HiddenInput]
dengan properti DisplayValue
. Ini digunakan untuk properti yang tidak perlu ditampilkan dalam bentuk dan daftar sama sekali ( DisplayValue=false
, digambar sebagai tag input
bertipe hidden
), atau untuk properti yang masih perlu ditampilkan, tetapi dalam bentuk teks yang tidak dapat diubah ( DisplayValue=true
, digambar seperti teks bersih, tanpa tag)[ScaffoldColumn]
- menunjukkan apakah akan menampilkan bidang dalam mengedit Html.EditorForModel
(misalnya, Html.EditorForModel
). Jika false
, maka deskripsi properti kelas atau nilainya tidak akan ditampilkan dalam formulir. [HiddenInput(DisplayValue = false)]
tidak dapat digunakan di [HiddenInput(DisplayValue = false)]
, karena dalam hal ini nilai-nilai properti kelas ini tidak akan ditampilkan sama sekali tidak hanya dalam formulir input informasi, tetapi juga dalam tampilan tabel. Dalam hal ini, ini diperlukan untuk properti LastLoginDate
, yang tidak dimasukkan secara manual, tetapi diisi di suatu tempat secara otomatis, tetapi kita masih perlu melihatnya.[Required]
- untuk memeriksa apakah suatu nilai telah dimasukkan untuk properti kelas, dengan teks pesan kesalahan di properti ErrorMessage
dan properti AllowEmptyStrings
memungkinkan Anda untuk memasukkan baris kosong.[EmailAddress]
- pada dasarnya atribut yang sama untuk memeriksa kebenaran alamat surat.
Kelas-kelas model database siap, kita beralih ke representasi (kelas-kelas model untuk representasi akan dijelaskan di bawah).
V - berarti kinerja pembalasan dendam
Di direktori
Views , buat direktori
Users untuk tampilan kami. Semua tampilan kami menggunakan standar (didefinisikan dalam file
_ViewStart.cshtml di direktori
Views )
_Layout.cshtml layout yang terletak di direktori
Views \ Shared . Buat tampilan
Index
(file
Index.cshtml di direktori
Users ):

Dan tulis kodenya:
@model Example_Users.Models.UsersGrid @{ ViewBag.Title = "Users page"; } @using (@Html.BeginForm()) { <div> <h3>Users list:</h3> @if (TempData["message"] != null) {<div class="text-success">@TempData["message"]</div>} @if (TempData["error"] != null) {<div class="text-warning"><span class="alert">ERROR:</span> @TempData["error"]</div>} @Html.Partial("List") <p> <input type="submit" name="onNewUser" value="New user" /> @*@Html.ActionLink("New user", "New", "Users")*@ </p> </div> }
Jika kami ingin tampilan ini dimulai secara default, maka buat perubahan ke
Default
di file
RouteConfig.cs :
routes.MapRoute(name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Users", action = "Index", id = UrlParameter.Optional });
Dalam tampilan itu sendiri, Anda perlu memperhatikan baris dengan
Html.Partial("List")
. Ini diperlukan untuk menggambar di tempat ini tampilan parsial umum terpisah khusus yang terletak di file
List.cshtml di direktori
Views \ Shared . Sebenarnya, ini adalah tabel kisi untuk menampilkan data dari tabel basis data
users
kami:
@model Example_Users.Models.UsersGrid @using Example_Users.Domain <div class="table-responsive"> <table class="table table-bordered table-hover"> <thead> <tr> <th>@Html.ActionLink(Html.DisplayNameFor(m => Model.Users.First().Loginname).ToString(), "Index", Request.QueryString.ToRouteValueDictionary("sortOrder", Model.SortingInfo.NewOrder(Html.NameFor(m => Model.Users.First().Loginname).ToString()))) @Html.SortIdentifier(Model.SortingInfo.currentSort, Html.NameFor(m => Model.Users.First().Loginname).ToString()) </th> <th>@Html.ActionLink(Html.DisplayNameFor(m => Model.Users.First().Language.LanguageName).ToString(), "Index", Request.QueryString.ToRouteValueDictionary("sortOrder", Model.SortingInfo.NewOrder(Html.NameFor(m => Model.Users.First().Language).ToString()))) @Html.SortIdentifier(Model.SortingInfo.currentSort, Html.NameFor(m => Model.Users.First().Language).ToString()) </th> <th>@Html.ActionLink(Html.DisplayNameFor(m => Model.Users.First().Email).ToString(), "Index", Request.QueryString.ToRouteValueDictionary("sortOrder", Model.SortingInfo.NewOrder(Html.NameFor(m => Model.Users.First().Email).ToString()))) @Html.SortIdentifier(Model.SortingInfo.currentSort, Html.NameFor(m => Model.Users.First().Email).ToString()) </th> <th>@Html.ActionLink(Html.DisplayNameFor(m => Model.Users.First().SupporterTier).ToString(), "Index", Request.QueryString.ToRouteValueDictionary("sortOrder", Model.SortingInfo.NewOrder(Html.NameFor(m => Model.Users.First().SupporterTier).ToString()))) @Html.SortIdentifier(Model.SortingInfo.currentSort, Html.NameFor(m => Model.Users.First().SupporterTier).ToString()) </th> <th>@Html.ActionLink(Html.DisplayNameFor(m => Model.Users.First().LastLoginDate).ToString(), "Index", Request.QueryString.ToRouteValueDictionary("sortOrder", Model.SortingInfo.NewOrder(Html.NameFor(m => Model.Users.First().LastLoginDate).ToString()))) @Html.SortIdentifier(Model.SortingInfo.currentSort, Html.NameFor(m => Model.Users.First().LastLoginDate).ToString()) </th> </tr> </thead> <tbody> @foreach (var item in Model.Users) { <tr> <td>@Html.ActionLink(item.Loginname, "Edit", "Users", new { UserID = item.UserID }, null)</td> <td>@Html.DisplayFor(modelitem => item.Language.LanguageName)</td> <td>@Html.DisplayFor(modelitem => item.Email)</td> <td class="@Html.DisplayFor(modelitem => item.SupporterTier)">@if (item.SupporterTier != Supporter.None) {@Html.DisplayFor(modelitem => item.SupporterTier);}</td> <td>@if (item.IsLastLogin) {@Html.DisplayFor(modelitem => item.LastLoginDate)}</td> </tr> } </tbody> </table> </div> @if (Model.PagingInfo.totalPages > 1) { <ul class="pagination"> @Html.PageLinks(Model.PagingInfo, x => Url.Action("Index", new { page = x, sortOrder = Model.SortingInfo.currentSort })) </ul> }
Dapat dilihat bahwa di header tabel data,
Html.DisplayNameFor
helper
Html.DisplayNameFor
untuk menampilkan nama-nama kolom dan untuk ini Anda harus menentukan referensi ke properti objek kelas. Karena ketika membentuk judul tabel kita hanya memiliki objek
Model.Users
, yang merupakan daftar objek bertipe
UserClass
, kita harus menggunakan metode berikut: pilih baris pertama daftar ini sebagai objek dari kelas
UserClass
. Misalnya, untuk nama pengguna:
Model.Users.First().Loginname
. Karena atribut
Loginname
dari kelas
Users
memiliki atribut
[Display(Name = "Login")]
, itu akan ditampilkan "Login" di nama kolom:

Apa lagi yang menarik dalam tampilan
List
? Blok
foreach
, tentu saja, membuat objek dari kelas
UserClass
yang ada di daftar
Users
diterima dari controller. Dan objek
SortingInfo
dan
PagingInfo
dalam model
UsersGrid
kami menarik di sini. Dan kita membutuhkan objek-objek ini untuk mengatur penyortiran data (digunakan pada header tabel pada tag
<th>
) dan mengatur pagination informasi (digunakan di bagian bawah halaman, di bawah tabel). Itu sebabnya kami tidak menggunakan sebagai model daftar objek murni dari tipe
IEnumerable<UserClass>
. Dan sebagai model, kami menggunakan kelas
UsersGrid
, yang terletak di file
UsersGrid.cs di direktori
Model .
public class UsersGrid { public IEnumerable<UserClass> Users { get; set; } public PagingInfo PagingInfo { get; set; } public SortingInfo SortingInfo { get; set; } }
Dan
PagingInfo
dan
SortingInfo
sendiri dalam file
GridInfo.cs
di tempat yang sama.
public class PagingInfo {
Dan untuk digunakan dalam tampilan, pembantu khusus telah ditambahkan ke file
GridHelpers.cs (direktori
HtmlHelpers ):
public static class GridHelpers {
Karena kisi dengan data tanpa mengurutkan dan tanpa informasi paging adalah hal yang agak tidak berguna, dan tidak ada penolong standar untuk seluruh tabel data di ASP.NET MVC, Anda harus membuatnya sendiri (atau mengambil yang dibuat oleh orang lain). Dalam hal ini, saya melihat beberapa implementasi dalam buku ASP.NET MVC dan solusi yang disajikan di Internet. Selain itu, untuk beberapa alasan, solusi yang menggabungkan setidaknya penyortiran data dan pagination, baik tidak sama sekali, atau saya tidak menemukan. Saya harus memahami semua ini, menggabungkan dan memodifikasinya ke keadaan normal. Sebagai contoh, pagination dalam implementasi tersebut sering tidak memberikan output dari daftar halaman yang kurang lebih panjang - yah, bagaimana jika akan ada beberapa ribu halaman? Saya memberikan contoh tampilan untuk solusi yang disajikan di atas:

Kami juga membutuhkan tampilan untuk membuat dan memodifikasi data. Lihat untuk membuat objek tipe
UserClass
:

Dan kode tampilan akan terlihat seperti ini:
@model Example_Users.Models.UserModel @{ ViewBag.Title = "New " + Html.DisplayNameForModel().ToString().ToLower(); } <h2>@ViewBag.Title</h2> @using (@Html.BeginForm("New", "Users", FormMethod.Post)) { @Html.EditorFor(m => m.User); @Html.LabelFor(m => Model.User.Language)<br /> @Html.DropDownListFor(m => Model.User.Language.LanguageID, Model.SelectLanguages()) <span/>@Html.ValidationMessageFor(m => Model.User.Language.LanguageID)<br /> <br /> <p><input type="submit" name="action" value="Add" /> <input type="button" onclick="history.go(-1)" value="Cancel" /></p> @*<p>@Html.ActionLink("Back to list", "Index")</p>*@ }
Pandangan ini menunjukkan, misalnya, penggunaan helper
Html.EditorFor
sebagai sarana untuk menghasilkan tag untuk mengedit semua properti objek di kelas
UserClass
. Ini ditampilkan sebagai berikut:

Tampilan ini menggunakan kelas
UserModel
sebagai model, bukan
UserClass
secara langsung. Kelas
UserModel
sendiri terletak di file
UserModel.cs di direktori
Models :
public class UserModel { public UserClass User { get; set; } private IList<LanguageClass> Languages { get; set; } public UserModel() {} public UserModel(UserClass user, IList<LanguageClass> languages) { this.User = user; this.Languages = languages; } public IEnumerable<SelectListItem> SelectLanguages() { if (Languages != null) { return new SelectList(Languages, "LanguageID", "LanguageName"); } return null; } }
Objek
UserClass
itu sendiri dan daftar tambahan objek tipe
LanguageClass
termasuk dalam kelas ini. Kami membutuhkan daftar ini untuk membuat daftar drop-down bahasa, dengan bahasa pengguna saat ini dipilih di dalamnya:
@Html.DropDownListFor(m => Model.User.Language.LanguageID, Model.SelectLanguages(), "")
Helper ini menggunakan panggilan ke fungsi
SelectLanguages()
, yang mengubah daftar bahasa menjadi objek tipe
SelectList
dengan parameter pengidentifikasi dan nama baris yang telah ditetapkan. Menempatkan generasi objek ini ke tampilan akan salah, karena idenya adalah tidak mengetahui tentang ikatan ini dengan nama bidang.
SelectList
saja, akan mungkin untuk menghasilkan
SelectList
langsung di controller, tapi saya suka opsi dengan daftar pribadi objek kelas domain dan fungsi lebih.
Untuk menghasilkan daftar drop-down, kita harus menggunakan bantuan yang terpisah, karena pembantu
Html.EditorFor(m => m.User)
tidak akan menghasilkan markup pengeditan untuk objek yang disematkan tipe
LanguageClass
(ini bisa dilewati dengan menulis template umum untuk daftar drop-down, tetapi di sini kita kami tidak akan melakukan ini ...).
Dan karena kami menggunakan objek kelas
UserModel
dalam pandangan kami, yang mencakup objek lain dari kelas
UserClass
, kami tidak akan dapat menggunakan helper
Html.EditorForModel()
, karena helper tidak rekursif dan tidak akan berfungsi dalam situasi ini, oleh karena itu helper
Html.EditorFor()
digunakan
Html.EditorFor()
untuk objek
User
.
:
Html.ActionLink("Back to list", "Index")
. , -, , –
button
, - . -, – –
UserClass
, . – :
/>
, . , , (, , , – ), .
UserClass
:

:
@model Example_Users.Models.UserModel @{ ViewBag.Title = "Edit " + Html.DisplayNameForModel().ToString().ToLower(); } <h2>@ViewBag.Title @Model.User.Loginname</h2> @using (@Html.BeginForm("Edit", "Users", FormMethod.Post)) { @Html.HiddenFor(m => Model.User.UserID) <div> @Html.LabelFor(m => Model.User.Loginname) @Html.EditorFor(m => Model.User.Loginname) @Html.ValidationMessageFor(m => Model.User.Loginname) </div> <div> @Html.LabelFor(m => Model.User.Language) @Html.DropDownListFor(m => Model.User.Language.LanguageID, Model.SelectLanguages()) @Html.ValidationMessageFor(m => Model.User.Language.LanguageID) </div> <div> @Html.LabelFor(m => Model.User.Email) @Html.EditorFor(m => Model.User.Email) @Html.ValidationMessageFor(m => Model.User.Email) </div> <div> @Html.LabelFor(m => Model.User.SupporterTier) @Html.EditorFor(m => Model.User.SupporterTier) @*@Html.EnumDropDownListFor(m => Model.User.SupporterTier)*@ @*@Html.DropDownListFor(m => m.Model.User.SupporterTier, new SelectList(Enum.GetNames(typeof(Example_Users.Domain.Supporter))))*@ @Html.ValidationMessageFor(m => Model.User.SupporterTier) </div> <br /> <p><input type="submit" name="action" value="Save" /> <input type="submit" name="action" value="Remove" onclick="javascript:return confirm('Are you sure?');" /> <input type="submit" name="action" value="Cancel" /></p> }
. – :

. :
/>
, (
Save ,
Remove ):
/>
, ( . ).
Enum
,
enum . ,
Html.EditorFor()
, (
<input type=”text”/>
), - (..
<select>
<option>
).
1.
Html.DropDownListFor()
Html.ListBoxFor()
, , :
@Html.DropDownListFor(m => m.Model.User.SupporterTier, new SelectList(Enum.GetNames(typeof(Example_Users.Domain.Supporter))))
. –
Html.EditorForModel()
Html.EditorFor()
.
2.
Editor
.
Supporter.cshtml Views\Shared\EditorTemplates :
@model Example_Users.Domain.Supporter @Html.DropDownListFor(m => m, new SelectList(Enum.GetNames(Model.GetType()), Model.ToString()))
,
Html.EditorFor(m => Model.SupporterTier)
,
Html.EditorFor(m => Model.SupporterTier, "Supporter")
. -,
[UIHint("Supporter")]
. ,
Html.EditorForModel()
–
Html.EditorFor(m => Model.SupporterTier, "Supporter")
.
- , (
New
)
Html.EditorFor()
Html.EditorForModel()
: «
, , NULL, «Example_Users.Domain.Supporter», NULL. » – , . .
3.
Html.EnumDropDownListFor()
, . , , , . «»:
Html.EditorForModel()
Html.EditorFor()
, ,
Html.EnumDropDownListFor()
. , , –
[UIHint]
,
[DataType]
[EnumDataType]
. , None , , ,
Supporter
.4. Sebagai hasilnya, saya datang dengan opsi solusi yang ditemukan di Internet : membuat template umum untuk transfer. Buat file Enum.cshtml di folder Views \ Shared \ EditorTemplates : @using System.ComponentModel.DataAnnotations @model Enum @{ Func<object, string> GetDisplayName = o => { var result = null as string; var display = o.GetType() .GetMember(o.ToString()).First() .GetCustomAttributes(false) .OfType<DisplayAttribute>() .LastOrDefault(); if (display != null) result = display.GetName(); return result ?? o.ToString(); }; var values = Enum.GetValues(ViewData.ModelMetadata.ModelType).Cast<object>() .Select(v => new SelectListItem { Selected = v.Equals(Model), Text = GetDisplayName(v), // v.ToString(), Value = v.ToString() }); } @Html.DropDownList("", values)
Di sini, secara umum, semuanya menjadi baik: templat berfungsi dengan baik jika memungkinkan. Anda bahkan [UIHint("Enum")]
tidak bisa menambahkan. Selain itu, template umum ini membaca atribut [Display(Name)]
untuk nilai enumerasi menggunakan fungsi khusus.C berarti pengontrol
Tambahkan file UsersController.cs ke direktori Controllers . public class UsersController : Controller { public int pageSize = 10; public int showPages = 15; public int count = 0;
:
1)
Index
:
public ViewResult Index(string sortOrder, int page = 1)
sortOrder
page
.
page
- ,
sortOrder
, SQL- . ( ,
Base.cs ):
sortOrder = Base.parseSortForDB(sortOrder, out sortName, out sortDir);
,
UsersRepository
( , ),
List
UsersGrid
. totalItem
List
,
UsersGrid
. .
Index
[HttpPost]
,
New
Index
:
public ActionResult Index(string onNewUser)
onNewUser
/>
Index
,
null .
submit
Index
, ( «New user»).
UserModel
,
UserClass
New
.
LanguageRepository
List
:
public IList<LanguageClass> Languages() { LanguagesRepository rep = new LanguagesRepository(); return rep.List(); }
2)
New
New
New User Html.ActionLink("New user", "New", "Users")
Index
. (
New
) , , .
New
model
UserModel
:
public ActionResult New(UserModel model)
New
UserModel
, ( ,
UserClass
LanguageClass
),
UsersRepository
,
AddUser(model.User)
. (
New
) .
3)
Edit
Edit
UserID
( ) :
public ActionResult Edit(int UserID)
UsersRepository
FetchByID(UserID)
UserClass
. –
UserModel
Edit
.
UserModel
action
:
public ActionResult Edit(UserModel model, string action)
UserModel
( )
Edit
. ,
action
, HTML-
input
.
value
.
Cancel
, . ,
New
,
Edit
.
ActionFilter
:
ReferrerHoldAttribute
ReferrerHoldAttribute.cs (
HtmlAttribute ):
public class ReferrerHoldAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { var referrer = filterContext.RequestContext.HttpContext.Request.UrlReferrer; if (referrer != null) filterContext.RouteData.Values.Add("referrer", referrer); base.OnActionExecuting(filterContext); } }
, «
New user » :
TempData["referrer"] = ControllerContext.RouteData.Values["referrer"];
, . , , .
, ,
New
Edit
, , :
if (TempData["referrer"] != null) return Redirect(TempData["referrer"].ToString());
action
Save Remove ,
UsersRepository
ChangeUser(model.User)
RemoveUser(model.User)
. (
Edit
) .
ADO.NET MySQL –
- .
Users
, , , ,
Languages
.
MySQL.Data MySqlCommand
:
using (MySqlConnection connect = new MySqlConnection( )) { string sql = " "; using (MySqlCommand cmd = new MySqlCommand(sql, connect)) { cmd.Parameters.Add(" ", ).Value = ; connect.Open(); result = cmd.ExecuteNonQuery() >= 0;
MySqlDataReader
, :
using (MySqlConnection connect = new MySqlConnection( )) { string sql = " "; using (MySqlDataReader dr = cmd.ExecuteReader()) { cmd.Parameters.Add(" ", ).Value = ; objConnect.Open(); while (dr.Read()) {
UserRepository.cs Models :
public class UsersRepository { public bool AddUser(UserClass user) { user.UserID = AddUser(Name: user.Loginname, LanguageID: user.Language.LanguageID, Email: user.Email, SupporterTier: user.SupporterTier); return user.UserID > 0; } public int AddUser(string Name, int LanguageID, string Email, Supporter SupporterTier) { int ID = 0; using (MySqlConnection connect = new MySqlConnection(Base.strConnect)) { string sql = "INSERT INTO `Users` (`Loginname`, `LanguageID`, `Email`, `SupporterTier`) VALUES (@Loginname, @LanguageID, @Email, @SupporterTier)"; using (MySqlCommand cmd = new MySqlCommand(sql, connect)) { cmd.Parameters.Add("Loginname", MySqlDbType.String).Value = Name; cmd.Parameters.Add("LanguageID", MySqlDbType.Int32).Value = LanguageID; cmd.Parameters.Add("Email", MySqlDbType.String).Value = Email; cmd.Parameters.Add("SupporterTier", MySqlDbType.Int32).Value = SupporterTier; connect.Open(); if (cmd.ExecuteNonQuery() >= 0) { sql = "SELECT LAST_INSERT_ID() AS ID"; cmd.CommandText = sql; int.TryParse(cmd.ExecuteScalar().ToString(), out ID); } } } return ID; } public bool ChangeUser(UserClass user) { return ChangeUser(ID: user.UserID, Name: user.Loginname, LanguageID: user.Language.LanguageID, Email: user.Email, SupporterTier: user.SupporterTier); } public bool ChangeUser(int ID, string Name, int LanguageID, string Email, Supporter SupporterTier) { bool result = false; if (ID > 0) { using (MySqlConnection connect = new MySqlConnection(Base.strConnect)) { string sql = "UPDATE `Users` SET `Loginname`=@Loginname, `LanguageID`=@LanguageID, `Email`=@Email, `SupporterTier`=@SupporterTier WHERE UserID=@UserID"; using (MySqlCommand cmd = new MySqlCommand(sql, connect)) { cmd.Parameters.Add("UserID", MySqlDbType.Int32).Value = ID; cmd.Parameters.Add("Loginname", MySqlDbType.String).Value = Name; cmd.Parameters.Add("LanguageID", MySqlDbType.Int32).Value = LanguageID; cmd.Parameters.Add("Email", MySqlDbType.String).Value = Email; cmd.Parameters.Add("SupporterTier", MySqlDbType.Int32).Value = SupporterTier; connect.Open(); result = cmd.ExecuteNonQuery() >= 0; } } } return result; } public bool RemoveUser(UserClass user) { return RemoveUser(user.UserID); } public bool RemoveUser(int ID) { using (MySqlConnection connect = new MySqlConnection(Base.strConnect)) { string sql = "DELETE FROM `Users` WHERE `UserID`=@UserID"; using (MySqlCommand cmd = new MySqlCommand(sql, connect)) { cmd.Parameters.Add("UserID", MySqlDbType.Int32).Value = ID; connect.Open(); return cmd.ExecuteNonQuery() >= 0; } } } public UserClass FetchByID(int ID) { UserClass user = null; using (MySqlConnection objConnect = new MySqlConnection(Base.strConnect)) { string strSQL = "SELECT u.`UserID`, u.`Loginname`, l.`LanguageID`, l.`LanguageName`, u.`Email`, u.`LastLoginDate`, CAST(u.`SupporterTier` AS UNSIGNED) as `SupporterTier` FROM `Users` u LEFT JOIN `Languages` l ON l.LanguageID=u.LanguageID WHERE `UserID`=@UserID"; using (MySqlCommand cmd = new MySqlCommand(strSQL, objConnect)) { objConnect.Open(); int UserID = 0, LanguageID = 0; string Loginname = null, LanguageName= null, Email = String.Empty; Supporter SupporterTier = Supporter.None; DateTime? LastLoginDate = null; cmd.Parameters.Add("UserID", MySqlDbType.Int32).Value = ID; using (MySqlDataReader dr = cmd.ExecuteReader()) { if (dr.Read()) { UserID = dr.GetInt32("UserID"); Loginname = dr.GetString("Loginname").ToString(); LanguageID = dr.GetInt32("LanguageID"); LanguageName = dr.GetString("LanguageName").ToString(); if (!dr.IsDBNull(dr.GetOrdinal("Email"))) Email = dr.GetString("Email").ToString(); if (!dr.IsDBNull(dr.GetOrdinal("LastLoginDate"))) LastLoginDate = dr.GetDateTime("LastLoginDate"); if (!dr.IsDBNull(dr.GetOrdinal("SupporterTier"))) SupporterTier = (Supporter)dr.GetInt32("SupporterTier"); } LanguageClass language = null; if (LanguageID > 0) language = new LanguageClass(LanguageID: LanguageID, LanguageName: LanguageName); if (UserID > 0 && language != null && language.LanguageID > 0) user = new UserClass(UserID: UserID, Loginname: Loginname, Language: language, Email: Email, LastLoginDate: LastLoginDate, SupporterTier: (Supporter)SupporterTier); } } } return user; }
AddUser
, (
ChangeUser
), (
RemoveUser
), (
FetchByID
)
List
.
List
:
- (
DataTable
), – . Yaitu SQL-. UserClass
. LIMIT
SQL- SELECT
. , MySQL , LIMIT
. , , ORDER BY
. , SQL-, SQL , , . , , : .- SQL
SELECT FOUND_ROWS()
, , SELECT SQL_CALC_FOUND_ROWS
LIMIT
.
, .
LanguageRepository.cs Models :
public class LanguagesRepository { public IList<LanguageClass> List() { List<LanguageClass> languages = new List<LanguageClass>(); using (MySqlConnection objConnect = new MySqlConnection(Base.strConnect)) { string strSQL = "SELECT `LanguageID`, `LanguageName` as `Language` FROM `Languages` ORDER BY `LanguageName`"; using (MySqlCommand cmd = new MySqlCommand(strSQL, objConnect)) { objConnect.Open(); using (MySqlDataReader dr = cmd.ExecuteReader()) { while (dr.Read()) { LanguageClass language = new LanguageClass(LanguageID: dr.GetInt32("LanguageID"), LanguageName: dr.GetString("Language").ToString()); languages.Add(language); } } } } return languages; } }
,
LanguageClass
.
Total
– . , : , , , .. dll. - - . «» , , ASP.NET MVC ADO.NET , MS SQL.
PS : ,
.
. —
.