譯文,個人原創(chuàng),轉(zhuǎn)載請注明出處(C# 6 與 .NET Core 1.0 高級編程 - 41 ASP.NET MVC(下)),不對的地方歡迎指出與交流。
章節(jié)出自《Professional C# 6 and .NET Core 1.0》。水平有限,各位閱讀時仔細分辨,唯望莫誤人子弟。
附英文版原文:Professional C# 6 and .NET Core 1.0 - Chapter 41 ASP.NET MVC
C# 6 與 .NET Core 1.0 高級編程 - 41 ASP.NET MVC(上)
C# 6 與 .NET Core 1.0 高級編程 - 41 ASP.NET MVC(中)
-------------------------
最近兩篇譯文來得比較遲,前一陣子忙起來之后忘記了。
由于有點事情,《Professional C# 6 and .NET Core 1.0》第42、43章譯文,是4月中旬之后的事情了。
Enjoy your reading, enjoy your code!
-------------------------
實現(xiàn)操作過濾器
ASP.NET MVC在許多領(lǐng)域是可擴展的。例如,可以實現(xiàn)控制器工廠來搜索和實例化控制器(接口IControllerFactory)。控制器實現(xiàn) IController 接口。在控制器中查找操作方法可以通過使用IActionInvoker接口來解決??梢允褂脧腁ctionMethodSelectorAttribute派生的屬性類來定義允許的HTTP方法。將HTTP請求映射到參數(shù)的模型綁定器可以通過實現(xiàn)IModelBinder接口自定義。 “模型綁定器”部分使用FormCollectionModelBinder類型。可以使用實現(xiàn)接口 IViewEngine 的不同視圖引擎。本章使用Razor視圖引擎。還可以通過HTML輔助程序、標記助手和操作過濾器進行自定義。大多數(shù)擴展點都超出了本書的范圍,但是操作過濾器是最經(jīng)常實現(xiàn)或使用的,因此這里將介紹這些過濾器。
在執(zhí)行操作之前和之后調(diào)用操作過濾器。它們被分配給使用屬性的控制器或控制器的動作方法。操作過濾器通過創(chuàng)建從基類ActionFilterAttribute派生的類來實現(xiàn)。這個類可以覆蓋基類成員OnActionExecuting,OnActionExecuted,OnResultExecuting和OnResultExecuted。 OnActionExecuting在調(diào)用action方法之前被調(diào)用,并且當action方法被完成時調(diào)用OnActionExecuted。之后,在返回結(jié)果之前,調(diào)用OnResultExecuting方法,最后調(diào)用OnResultExecuted。
在這些方法中,可以訪問Request對象以檢索調(diào)用者的信息。通過Request對象可以根據(jù)瀏覽器決定一些操作,可以訪問路由信息,可以動態(tài)更改視圖結(jié)果等等。以下代碼片段從路由信息訪問變量語言。要將此變量添加到路由,可以如本章前面的“定義路由”部分所述更改路由。通過在路由信息中添加語言變量,如下代碼片段所示可以使用 RouteData.Values 訪問URL提供的值。可以使用檢索到的值更改用戶語言:
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ū)域細節(jié)。
如以下代碼段所示,創(chuàng)建的操作過濾器屬性類可以將該屬性應(yīng)用于控制器。使用該類的屬性,每個action方法都調(diào)用屬性類的成員。另外,也可以將屬性應(yīng)用于操作方法,因此僅當調(diào)用操作方法時才調(diào)用成員:
[Language] public class HomeController : Controller {
ActionFilterAttribute實現(xiàn)幾個接口:IActionFilter,IAsyncActionFilter,IResultFilter,IAsyncResultFilter,IFilter和 IOrderedFilter。
ASP.NET MVC包括一些預(yù)定義的操作過濾器,如 請求 HTTPS 的過濾器,授權(quán)調(diào)用,處理錯誤或緩存數(shù)據(jù)。
將在本章后面的“驗證和授權(quán)”部分中介紹使用特性Authorize。
創(chuàng)建數(shù)據(jù)驅(qū)動的應(yīng)用程序
現(xiàn)在你已經(jīng)閱讀了ASP.NET MVC的所有基礎(chǔ),是時候來看一個使用ADO.NET實體框架的數(shù)據(jù)驅(qū)動的應(yīng)用程序??梢钥吹紸SP.NET MVC結(jié)合數(shù)據(jù)訪問提供的功能。
注意 ADO.NET實體框架在第38章“實體框架核心”中有詳細介紹。
示例應(yīng)用程序 MenuPlanner 用于維護在數(shù)據(jù)庫中的餐館菜單條目。只有經(jīng)過身份驗證的帳戶才可以執(zhí)行數(shù)據(jù)庫條目的維護。未經(jīng)身份驗證的用戶則可以瀏覽菜單。
該項目是通過使用 ASP.NET Core 1.0 Web 應(yīng)用程序模板創(chuàng)建的。身份驗證使用默認選擇的個人用戶帳戶。這個項目模板為ASP.NET MVC和控制器添加了幾個文件夾,包括HomeController和AccountController。它還添加了一些腳本庫。
定義模型
首先在 Models 目錄中定義一個模型。使用ADO.NET實體框架創(chuàng)建模型。 MenuCard類型定義了一些屬性和與菜單列表的關(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 引用的菜單類型由Menu類定義(代碼文件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ù)庫的連接,以及 Menu 和 MenuCard 類型的集合都由 MenuCardsContext 管理。使用ModelBuilder,上下文指定Menu類型的Text屬性不能為null,并且它的最大長度為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)用程序的啟動代碼定義了用作數(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實例 。當然可以改變這個,也可以添加一個到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ù)庫
可以使用Entity Framework命令來創(chuàng)建用于創(chuàng)建數(shù)據(jù)庫的代碼。命令行提示符中可以使用.NET核心命令行(CLI)和ef命令創(chuàng)建代碼以自動創(chuàng)建數(shù)據(jù)庫。要使用命令提示符,必須將當前文件夾設(shè)置為project.json文件所在的目錄:
>dotnet ef migrations add InitMenuCards --context MenuCardsContext
注意 dotnet工具在第1章“.NET應(yīng)用程序體系結(jié)構(gòu)”和第17章“Visual Studio 2015”中討論。
因為多個數(shù)據(jù)上下文( MenuCardsContext 和 ApplicationDbContext )是通過項目定義的,所以需要使用--context選項指定數(shù)據(jù)上下文。 ef命令在項目結(jié)構(gòu)創(chuàng)建一個Migrations文件夾, InitMenuCards類中使用Up方法創(chuàng)建數(shù)據(jù)庫表,使用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)在只需要一些代碼來啟動遷移進程,用初始樣本數(shù)據(jù)填充數(shù)據(jù)庫。 MenuCardDatabaseInitializer 通過在從 Database 屬性返回的DatabaseFacade對象上調(diào)用擴展方法 MigrateAsync 來應(yīng)用遷移過程。這反過來檢查與連接字符串相關(guān)聯(lián)的數(shù)據(jù)庫是否已具有與通過遷移指定的數(shù)據(jù)庫相同的版本。如果它不具有相同的版本,則調(diào)用所需的Up方法以獲得相同的版本。除此之外,創(chuàng)建幾個MenuCard對象將它們存儲在數(shù)據(jù)庫中(代碼文件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ù)庫和模型到位,可以創(chuàng)建一個服務(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ù)類MenuCardsService實現(xiàn)了返回菜單和菜單卡的方法,創(chuàng)建、更新和刪除菜單(代碼文件 MenuPlanner/Services/MenuCardsService.cs):
作者:沐汐 Vicky
出處:http://www.cnblogs.com/EasyInvoice
歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,否則保留追究法律責任的權(quán)利.
http://www.cnblogs.com/EasyInvoice/p/6464089.html