譯文,個(gè)人原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處(C# 6 與 .NET Core 1.0 高級(jí)編程 - 41 ASP.NET MVC(下)),不對(duì)的地方歡迎指出與交流。
章節(jié)出自《Professional C# 6 and .NET Core 1.0》。水平有限,各位閱讀時(shí)仔細(xì)分辨,唯望莫誤人子弟。
附英文版原文:Professional C# 6 and .NET Core 1.0 - Chapter 41 ASP.NET MVC
C# 6 與 .NET Core 1.0 高級(jí)編程 - 41 ASP.NET MVC(上)
C# 6 與 .NET Core 1.0 高級(jí)編程 - 41 ASP.NET MVC(中)
-------------------------
最近兩篇譯文來(lái)得比較遲,前一陣子忙起來(lái)之后忘記了。
由于有點(diǎn)事情,《Professional C# 6 and .NET Core 1.0》第42、43章譯文,是4月中旬之后的事情了。
Enjoy your reading, enjoy your code!
-------------------------
實(shí)現(xiàn)操作過(guò)濾器
ASP.NET MVC在許多領(lǐng)域是可擴(kuò)展的。例如,可以實(shí)現(xiàn)控制器工廠來(lái)搜索和實(shí)例化控制器(接口IControllerFactory)??刂破鲗?shí)現(xiàn) IController 接口。在控制器中查找操作方法可以通過(guò)使用IActionInvoker接口來(lái)解決??梢允褂脧腁ctionMethodSelectorAttribute派生的屬性類(lèi)來(lái)定義允許的HTTP方法。將HTTP請(qǐng)求映射到參數(shù)的模型綁定器可以通過(guò)實(shí)現(xiàn)IModelBinder接口自定義。 “模型綁定器”部分使用FormCollectionModelBinder類(lèi)型??梢允褂脤?shí)現(xiàn)接口 IViewEngine 的不同視圖引擎。本章使用Razor視圖引擎。還可以通過(guò)HTML輔助程序、標(biāo)記助手和操作過(guò)濾器進(jìn)行自定義。大多數(shù)擴(kuò)展點(diǎn)都超出了本書(shū)的范圍,但是操作過(guò)濾器是最經(jīng)常實(shí)現(xiàn)或使用的,因此這里將介紹這些過(guò)濾器。
在執(zhí)行操作之前和之后調(diào)用操作過(guò)濾器。它們被分配給使用屬性的控制器或控制器的動(dòng)作方法。操作過(guò)濾器通過(guò)創(chuàng)建從基類(lèi)ActionFilterAttribute派生的類(lèi)來(lái)實(shí)現(xiàn)。這個(gè)類(lèi)可以覆蓋基類(lèi)成員OnActionExecuting,OnActionExecuted,OnResultExecuting和OnResultExecuted。 OnActionExecuting在調(diào)用action方法之前被調(diào)用,并且當(dāng)action方法被完成時(shí)調(diào)用OnActionExecuted。之后,在返回結(jié)果之前,調(diào)用OnResultExecuting方法,最后調(diào)用OnResultExecuted。
在這些方法中,可以訪(fǎng)問(wèn)Request對(duì)象以檢索調(diào)用者的信息。通過(guò)Request對(duì)象可以根據(jù)瀏覽器決定一些操作,可以訪(fǎng)問(wèn)路由信息,可以動(dòng)態(tài)更改視圖結(jié)果等等。以下代碼片段從路由信息訪(fǎng)問(wèn)變量語(yǔ)言。要將此變量添加到路由,可以如本章前面的“定義路由”部分所述更改路由。通過(guò)在路由信息中添加語(yǔ)言變量,如下代碼片段所示可以使用 RouteData.Values 訪(fǎng)問(wèn)URL提供的值??梢允褂脵z索到的值更改用戶(hù)語(yǔ)言:
public class LanguageAttribute : ActionFilterAttribute { private string _language = null; public override void OnActionExecuting(ActionExecutingContext filterContext) { _language = filterContext.RouteData.Values["language"] == null ? null : filterContext.RouteData.Values["language"].ToString(); //… } public override void OnResultExecuting(ResultExecutingContext filterContext) { } }
注意 第28章“本地化”解釋了全球化和本地化,設(shè)置文化和其他區(qū)域細(xì)節(jié)。
如以下代碼段所示,創(chuàng)建的操作過(guò)濾器屬性類(lèi)可以將該屬性應(yīng)用于控制器。使用該類(lèi)的屬性,每個(gè)action方法都調(diào)用屬性類(lèi)的成員。另外,也可以將屬性應(yīng)用于操作方法,因此僅當(dāng)調(diào)用操作方法時(shí)才調(diào)用成員:
[Language] public class HomeController : Controller {
ActionFilterAttribute實(shí)現(xiàn)幾個(gè)接口:IActionFilter,IAsyncActionFilter,IResultFilter,IAsyncResultFilter,IFilter和 IOrderedFilter。
ASP.NET MVC包括一些預(yù)定義的操作過(guò)濾器,如 請(qǐng)求 HTTPS 的過(guò)濾器,授權(quán)調(diào)用,處理錯(cuò)誤或緩存數(shù)據(jù)。
將在本章后面的“驗(yàn)證和授權(quán)”部分中介紹使用特性Authorize。
創(chuàng)建數(shù)據(jù)驅(qū)動(dòng)的應(yīng)用程序
現(xiàn)在你已經(jīng)閱讀了ASP.NET MVC的所有基礎(chǔ),是時(shí)候來(lái)看一個(gè)使用ADO.NET實(shí)體框架的數(shù)據(jù)驅(qū)動(dòng)的應(yīng)用程序??梢钥吹紸SP.NET MVC結(jié)合數(shù)據(jù)訪(fǎng)問(wèn)提供的功能。
注意 ADO.NET實(shí)體框架在第38章“實(shí)體框架核心”中有詳細(xì)介紹。
示例應(yīng)用程序 MenuPlanner 用于維護(hù)在數(shù)據(jù)庫(kù)中的餐館菜單條目。只有經(jīng)過(guò)身份驗(yàn)證的帳戶(hù)才可以執(zhí)行數(shù)據(jù)庫(kù)條目的維護(hù)。未經(jīng)身份驗(yàn)證的用戶(hù)則可以瀏覽菜單。
該項(xiàng)目是通過(guò)使用 ASP.NET Core 1.0 Web 應(yīng)用程序模板創(chuàng)建的。身份驗(yàn)證使用默認(rèn)選擇的個(gè)人用戶(hù)帳戶(hù)。這個(gè)項(xiàng)目模板為ASP.NET MVC和控制器添加了幾個(gè)文件夾,包括HomeController和AccountController。它還添加了一些腳本庫(kù)。
定義模型
首先在 Models 目錄中定義一個(gè)模型。使用ADO.NET實(shí)體框架創(chuàng)建模型。 MenuCard類(lèi)型定義了一些屬性和與菜單列表的關(guān)系(代碼文件MenuPlanner/Models/MenuCard.cs):
public class MenuCard { public int Id { get; set; } [MaxLength(50)] public string Name { get; set; } public bool Active { get; set; } public int Order { get; set; } public virtual List<Menu> Menus { get; set; } }
從 MenuCard 引用的菜單類(lèi)型由Menu類(lèi)定義(代碼文件MenuPlanner/Models/Menu.cs):
public class Menu { public int Id { get; set; } public string Text { get; set; } public decimal Price { get; set; } public bool Active { get; set; } public int Order { get; set; } public string Type { get; set; } public DateTime Day { get; set; } public int MenuCardId { get; set; } public virtual MenuCard MenuCard { get; set; } }
與數(shù)據(jù)庫(kù)的連接,以及 Menu 和 MenuCard 類(lèi)型的集合都由 MenuCardsContext 管理。使用ModelBuilder,上下文指定Menu類(lèi)型的Text屬性不能為null,并且它的最大長(zhǎng)度為50(代碼文件MenuPlanner/Models/MenuCardsContext.cs):
public class MenuCardsContext : DbContext { public DbSet<Menu> Menus { get; set; } public DbSet<MenuCard> MenuCards { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Menu>().Property(p => p.Text) .HasMaxLength(50).IsRequired(); base.OnModelCreating(modelBuilder); } }
Web應(yīng)用程序的啟動(dòng)代碼定義了用作數(shù)據(jù)上下文的MenuCardsContext,并從配置文件讀取連接字符串(代碼文件MenuPlanner/Startup.cs):
public IConfiguration Configuration { get; set; } public void ConfigureServices(IServiceCollection services) { // Add Entity Framework services to the services container. services.AddEntityFramework() .AddSqlServer() .AddDbContext<ApplicationDbContext>(options => options.UseSqlServer( Configuration["Data:DefaultConnection:ConnectionString"])) .AddDbContext<MenuCardsContext>(options => options.UseSqlServer( Configuration["Data:MenuCardConnection:ConnectionString"])); // etc. }
配置文件添加 MenuCardConnection 連接字符串。 該連接字符串引用 Visual Studio 2015 附帶的SQL實(shí)例 。當(dāng)然可以改變這個(gè),也可以添加一個(gè)到SQL Azure 的連接字符串(代碼文件MenuPlanner/appsettings.json):
{ "Data": { "DefaultConnection": { "ConnectionString":"Server=(localdb)\\mssqllocaldb; Database=aspnet5-MenuPlanner-4d3d9092-b53f-4162-8627-f360ef6b2aa8; Trusted_Connection=True;MultipleActiveResultSets=true" }, "MenuCardConnection": { "ConnectionString":"Server= (localdb)\\mssqllocaldb;Database=MenuCards; Trusted_Connection=True;MultipleActiveResultSets=true" } }, // etc. }
創(chuàng)建數(shù)據(jù)庫(kù)
可以使用Entity Framework命令來(lái)創(chuàng)建用于創(chuàng)建數(shù)據(jù)庫(kù)的代碼。命令行提示符中可以使用.NET核心命令行(CLI)和ef命令創(chuàng)建代碼以自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)。要使用命令提示符,必須將當(dāng)前文件夾設(shè)置為project.json文件所在的目錄:
>dotnet ef migrations add InitMenuCards --context MenuCardsContext
注意 dotnet工具在第1章“.NET應(yīng)用程序體系結(jié)構(gòu)”和第17章“Visual Studio 2015”中討論。
因?yàn)槎鄠€(gè)數(shù)據(jù)上下文( MenuCardsContext 和 ApplicationDbContext )是通過(guò)項(xiàng)目定義的,所以需要使用--context選項(xiàng)指定數(shù)據(jù)上下文。 ef命令在項(xiàng)目結(jié)構(gòu)創(chuàng)建一個(gè)Migrations文件夾, InitMenuCards類(lèi)中使用Up方法創(chuàng)建數(shù)據(jù)庫(kù)表,使用Down方法再次刪除更改(代碼文件MenuPlanner/Migrations/[date] InitMenuCards.cs):
public partial class InitMenuCards : Migration { public override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name:"MenuCard", columns: table => new { Id = table.Column<int>(nullable: false) .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), Active = table.Column<bool>(nullable: false), Name = table.Column<string>(nullable: true), Order = table.Column<int>(nullable: false) }, constraints: table => { table.PrimaryKey("PK_MenuCard", x => x.Id); }); migrationBuilder.CreateTable( name:"Menu", columns: table => new { Id = table.Column<int>(nullable: false) .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), Active = table.Column<bool>(nullable: false), Day = table.Column<DateTime>(nullable: false), MenuCardId = table.Column<int>(nullable: false), Order = table.Column<int>(nullable: false), Price = table.Column<decimal>(nullable: false), Text = table.Column<string>(nullable: false), Type = table.Column<string>(nullable: true) }, constraints: table => { table.PrimaryKey("PK_Menu", x => x.Id); table.ForeignKey( name:"FK_Menu_MenuCard_MenuCardId", column: x => x.MenuCardId, principalTable:"MenuCard", principalColumn:"Id", onDelete: RefeerentialAction.Cascade); }); } public override void Down(MigrationBuilder migration) { migration.DropTable("Menu"); migration.DropTable("MenuCard"); } }
現(xiàn)在只需要一些代碼來(lái)啟動(dòng)遷移進(jìn)程,用初始樣本數(shù)據(jù)填充數(shù)據(jù)庫(kù)。 MenuCardDatabaseInitializer 通過(guò)在從 Database 屬性返回的DatabaseFacade對(duì)象上調(diào)用擴(kuò)展方法 MigrateAsync 來(lái)應(yīng)用遷移過(guò)程。這反過(guò)來(lái)檢查與連接字符串相關(guān)聯(lián)的數(shù)據(jù)庫(kù)是否已具有與通過(guò)遷移指定的數(shù)據(jù)庫(kù)相同的版本。如果它不具有相同的版本,則調(diào)用所需的Up方法以獲得相同的版本。除此之外,創(chuàng)建幾個(gè)MenuCard對(duì)象將它們存儲(chǔ)在數(shù)據(jù)庫(kù)中(代碼文件MenuPlanner/Models/MenuCardDatabaseInitializer.cs):
using Microsoft.EntityFrameworkCore; using System.Linq; using System.Threading.Tasks; namespace MenuPlanner.Models { public class MenuCardDatabaseInitializer { private static bool _databaseChecked = false; public MenuCardDatabaseInitializer(MenuCardsContext context) { _context = context; } private MenuCardsContext _context; public async Task CreateAndSeedDatabaseAsync() { if (!_databaseChecked) { _databaseChecked = true; await _context.Database.MigrateAsync(); if (_context.MenuCards.Count() == 0) { _context.MenuCards.Add( new MenuCard { Name ="Breakfast", Active = true, Order = 1 }); _context.MenuCards.Add( new MenuCard { Name ="Vegetarian", Active = true, Order = 2 }); _context.MenuCards.Add( new MenuCard { Name ="Steaks", Active = true, Order = 3 }); } await _context.SaveChangesAsync(); } } } }
隨著數(shù)據(jù)庫(kù)和模型到位,可以創(chuàng)建一個(gè)服務(wù)。
創(chuàng)建服務(wù)
在創(chuàng)建服務(wù)之前,創(chuàng)建接口IMenuCardsService,該接口定義服務(wù)所需的所有方法(代碼文件MenuPlanner/Services/IMenuCardsService.cs):
using MenuPlanner.Models; using System.Collections.Generic; using System.Threading.Tasks; namespace MenuPlanner.Services { public interface IMenuCardsService { Task AddMenuAsync(Menu menu); Task DeleteMenuAsync(int id); Task<Menu> GetMenuByIdAsync(int id); Task<IEnumerable<Menu>> GetMenusAsync(); Task<IEnumerable<MenuCard>> GetMenuCardsAsync(); Task UpdateMenuAsync(Menu menu); } }
服務(wù)類(lèi)MenuCardsService實(shí)現(xiàn)了返回菜單和菜單卡的方法,創(chuàng)建、更新和刪除菜單(代碼文件 MenuPlanner/Services/MenuCardsService.cs):
using MenuPlanner.Models; using Microsoft.EntityFrameworkCore using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace MenuPlanner.Services { public class MenuCardsService : IMenuCardsService { private MenuCardsContext _menuCardsContext; public MenuCardsService(MenuCardsContext menuCardsContext) { _menuCardsContext = menuCardsContext; } public async Task<IEnumerable<Menu>> GetMenusAsync() { await EnsureDatabaseCreated(); var menus = _menuCardsContext.Menus.Include(m => m.MenuCard); return await menus.ToArrayAsync(); } public async Task<IEnumerable<MenuCard>> GetMenuCardsAsync() { await EnsureDatabaseCreated(); var menuCards = _menuCardsContext.MenuCards; return await menuCards.ToArrayAsync(); } public async Task<Menu> GetMenuByIdAsync(int id) { return await _menuCardsContext.Menus.SingleOrDefaultAsync( m => m.Id == id); } public async Task AddMenuAsync(Menu menu) { _menuCardsContext.Menus.Add(menu); await _menuCardsContext.SaveChangesAsync(); } public async Task UpdateMenuAsync(Menu menu) { _menuCardsContext.Entry(menu).State = EntityState.Modified; await _menuCardsContext.SaveChangesAsync(); } public async Task DeleteMenuAsync(int id) { Menu menu = _menuCardsContext.Menus.Single(m => m.Id == id); _menuCardsContext.Menus.Remove(menu); await _menuCardsContext.SaveChangesAsync(); } private async Task EnsureDatabaseCreated() { var init = new MenuCardDatabaseInitializer(_menuCardsContext); await init.CreateAndSeedDatabaseAsync(); } } }
要通過(guò)依賴(lài)注入使服務(wù)可用,使用AddScoped方法將服務(wù)注冊(cè)到服務(wù)集合中(代碼文件MenuPlanner/Startup.cs):
public void ConfigureServices(IServiceCollection services) { // etc. services.AddScoped<IMenuCardsService, MenuCardsService>(); // etc. }
創(chuàng)建控制器
ASP.NET MVC提供了構(gòu)架來(lái)創(chuàng)建直接訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)的控制器??梢酝ㄟ^(guò)在解決方案資源管理器中選擇Controllers文件夾來(lái)執(zhí)行此操作,并從上下文菜單中選擇添加->控制器。將打開(kāi)“添加構(gòu)架”對(duì)話(huà)框。從“添加構(gòu)架”對(duì)話(huà)框中,可以使用Entity Framework選擇“MVC 6控制器”視圖。單擊添加按鈕將打開(kāi)添加控制器對(duì)話(huà)框,如圖41.13所示。該對(duì)話(huà)框可以選擇 Menu 模型類(lèi)和實(shí)體框架數(shù)據(jù)上下文MenuCardsContext,配置生成視圖,并給控制器命名。創(chuàng)建具有視圖的控制器以查看生成的代碼,以及視圖。
圖41.13
本書(shū)示例不直接使用來(lái)自控制器的數(shù)據(jù)上下文,而是在其間插入服務(wù)。這樣做提供了更多的靈活性??梢允褂脕?lái)自不同控制器的服務(wù),同時(shí)可以使用來(lái)自諸如ASP.NET Web API之類(lèi)的服務(wù)的服務(wù)。
注意 ASP.NET Web API在第42章討論。
通過(guò)以下示例代碼,ASP.NET MVC控制器通過(guò)構(gòu)造函數(shù)注入注入菜單卡服務(wù)(代碼文件MenuPlanner/Controllers/MenuAdminController.cs):
public class MenuAdminController : Controller { private readonly IMenuCardsService _service; public MenuAdminController(IMenuCardsService service) { _service = service; } // etc. }
Index方法是當(dāng)僅使用URL引用控制器而不傳遞操作方法時(shí)調(diào)用的默認(rèn)方法。此處,將創(chuàng)建數(shù)據(jù)庫(kù)中的所有 Menu 項(xiàng),并將其傳遞到 Index 視圖。 Details 方法返回通過(guò)從服務(wù)找到的菜單的Details視圖。注意錯(cuò)誤處理。當(dāng)沒(méi)有ID傳遞給Details方法時(shí),使用來(lái)自基類(lèi)的HttpBadRequest方法返回HTTP Bad Request(400錯(cuò)誤響應(yīng))。當(dāng)在數(shù)據(jù)庫(kù)中找不到菜單ID時(shí),通過(guò)HttpNotFound方法返回HTTP Not Found(404錯(cuò)誤響應(yīng)):
public async Task<IActionResult> Index() { return View(await _service.GetMenusAsync()); } public async Task<IActionResult> Details(int? id = 0) { if (id == null) { return HttpBadRequest(); } Menu menu = await _service.GetMenuByIdAsync(id.Value); if (menu == null) { return HttpNotFound(); } return View(menu); }
當(dāng)用戶(hù)創(chuàng)建新菜單時(shí),在來(lái)自客戶(hù)端的HTTP GET請(qǐng)求之后調(diào)用第一個(gè)Create方法。使用該方法,ViewBag信息將傳遞到視圖。ViewBag包含有關(guān)SelectList中的菜單卡的信息。 SelectList允許用戶(hù)選擇項(xiàng)目。因?yàn)镸enuCard集合被傳遞給SelectList,所以用戶(hù)可以用新創(chuàng)建的菜單選擇菜單卡。
public async Task<IActionResult> Create() { IEnumerable<MenuCard> cards = await _service.GetMenuCardsAsync(); ViewBag.MenuCardId = new SelectList(cards,"Id","Name"); return View(); }
注意 要使用SelectList類(lèi)型,必須將NuGet包Microsoft.AspNet.Mvc.ViewFeatures添加到項(xiàng)目。
在用戶(hù)填寫(xiě)表單并將具有新菜單的表單提交給服務(wù)器后,第二個(gè)Create方法從HTTP POST請(qǐng)求中調(diào)用。該方法使用模型綁定將表單數(shù)據(jù)傳遞到Menu對(duì)象,并將Menu對(duì)象添加到數(shù)據(jù)上下文以將新創(chuàng)建的菜單寫(xiě)入數(shù)據(jù)庫(kù):
[HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> Create( [Bind("Id","MenuCardId","Text","Price","Active","Order","Type","Day")] Menu menu) { if (ModelState.IsValid) { await _service.AddMenuAsync(menu); return RedirectToAction("Index"); } IEnumerable<MenuCard> cards = await _service.GetMenuCardsAsync(); ViewBag.MenuCards = new SelectList(cards,"Id","Name"); return View(menu); }
要編輯菜單卡,需要定義兩個(gè)名為Edit的操作方法 - 一個(gè)用于GET請(qǐng)求,一個(gè)用于POST請(qǐng)求。第一個(gè)Edit方法返回單個(gè)菜單項(xiàng),第二個(gè)在成功完成模型綁定后調(diào)用服務(wù)的UpdateMenuAsync方法:
public async Task<IActionResult> Edit(int? id) { if (id == null) { return HttpBadRequest(); } Menu menu = await _service.GetMenuByIdAsync(id.Value); if (menu == null) { return HttpNotFound(); } IEnumerable<MenuCard> cards = await _service.GetMenuCardsAsync(); ViewBag.MenuCards = new SelectList(cards,"Id","Name", menu.MenuCardId); return View(menu); } [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Edit( [Bind("Id","MenuCardId","Text","Price","Order","Type","Day")] Menu menu) { if (ModelState.IsValid) { await _service.UpdateMenuAsync(menu); return RedirectToAction("Index"); } IEnumerable<MenuCard> cards = await _service.GetMenuCardsAsync(); ViewBag.MenuCards = new SelectList(cards,"Id","Name", menu.MenuCardId); return View(menu); }
控制器的最后一部分包括 Delete 方法。因?yàn)閮蓚€(gè)方法都有相同的參數(shù) - C#中這是不允許的,第二個(gè)方法的名稱(chēng)改為DeleteConfirmed。但是,第二個(gè)方法可以從與第一個(gè)Delete方法相同的URL鏈接訪(fǎng)問(wèn),但第二個(gè)方法使用HTTP POST訪(fǎng)問(wèn)而不是使用ActionName特性的GET訪(fǎng)問(wèn)。該方法調(diào)用服務(wù)的DeleteMenuAsync方法:
public async Task<IActionResult> Delete(int? id) { if (id == null) { return HttpBadRequest(); } Menu menu = await _service.GetMenuByIdAsync(id.Value); if (menu == null) { return HttpNotFound(); } return View(menu); } [HttpPost, ActionName("Delete")] [ValidateAntiForgeryToken] public async Task<IActionResult> DeleteConfirmed(int id) { Menu menu = await _service.GetMenuByIdAsync(id); await _service.DeleteMenuAsync(menu.Id); return RedirectToAction("Index"); }
創(chuàng)建視圖
現(xiàn)在是時(shí)候創(chuàng)建視圖了。視圖在 Views/MenuAdmin 文件夾中創(chuàng)建??梢酝ㄟ^(guò)在解決方案資源管理器中選擇MenuAdmin文件夾來(lái)創(chuàng)建視圖,然后從上下文菜單中選擇添加->視圖。打開(kāi)“添加視圖”對(duì)話(huà)框,如圖41.14所示。對(duì)話(huà)框中可以選擇列表、詳細(xì)信息、創(chuàng)建、編輯、刪除模板,然后相應(yīng)地安排HTML元素。使用此對(duì)話(huà)框選擇的Model類(lèi)指定了視圖基于的模型。
圖41.14
定義HTML表的 Index 視圖具有作為其模型的菜單集合。對(duì)于表的頭元素,帶有標(biāo)記助手asp-for的HTML元素標(biāo)簽用于訪(fǎng)問(wèn)要顯示的屬性名稱(chēng)。為了顯示條目,使用@foreach迭代菜單集合,并且使用輸入元素的Tag Helper訪(fǎng)問(wèn)每個(gè)屬性值。錨元素的標(biāo)記助手會(huì)為“編輯”、“詳細(xì)信息”和“刪除”頁(yè)面創(chuàng)建鏈接(代碼文件MenuPlanner/Views/MenuAdmin/Index.cshtml):
@model IList<MenuPlanner.Models.Menu> @{ ViewBag.Title ="Index"; } <h2>@ViewBag.Title</h2> <p> <a asp-action="Create">Create New</a> </p> @if (Model.Count() > 0) { <table> <tr> <th> <label asp-for="@Model[0].MenuCard.Item"></label> </th> <th> <label asp-for="@Model[0].Text"></label> </th> <th> <label asp-for="Model[0].Day"></label> </th> </tr>
</label>