译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(下)),不对的地方欢迎指出与交流。
章节出自《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!
-------------------------
实现操作过滤器
ASP.NET MVC在许多领域是可扩展的。例如,可以实现控制器工厂来搜索和实例化控制器(接口IControllerFactory)。控制器实现 IController 接口。在控制器中查找操作方法可以通过使用IActionInvoker接口来解决。可以使用从ActionMethodSelectorAttribute派生的属性类来定义允许的HTTP方法。将HTTP请求映射到参数的模型绑定器可以通过实现IModelBinder接口自定义。 “模型绑定器”部分使用FormCollectionModelBinder类型。可以使用实现接口 IViewEngine 的不同视图引擎。本章使用Razor视图引擎。还可以通过HTML辅助程序、标记助手和操作过滤器进行自定义。大多数扩展点都超出了本书的范围,但是操作过滤器是最经常实现或使用的,因此这里将介绍这些过滤器。
在执行操作之前和之后调用操作过滤器。它们被分配给使用属性的控制器或控制器的动作方法。操作过滤器通过创建从基类ActionFilterAttribute派生的类来实现。这个类可以覆盖基类成员OnActionExecuting,OnActionExecuted,OnResultExecuting和OnResultExecuted。 OnActionExecuting在调用action方法之前被调用,并且当action方法被完成时调用OnActionExecuted。之后,在返回结果之前,调用OnResultExecuting方法,最后调用OnResultExecuted。
在这些方法中,可以访问Request对象以检索调用者的信息。通过Request对象可以根据浏览器决定一些操作,可以访问路由信息,可以动态更改视图结果等等。以下代码片段从路由信息访问变量语言。要将此变量添加到路由,可以如本章前面的“定义路由”部分所述更改路由。通过在路由信息中添加语言变量,如下代码片段所示可以使用 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章“本地化”解释了全球化和本地化,设置文化和其他区域细节。
如以下代码段所示,创建的操作过滤器属性类可以将该属性应用于控制器。使用该类的属性,每个action方法都调用属性类的成员。另外,也可以将属性应用于操作方法,因此仅当调用操作方法时才调用成员:
[Language] public class HomeController : Controller {
ActionFilterAttribute实现几个接口:IActionFilter,IAsyncActionFilter,IResultFilter,IAsyncResultFilter,IFilter和 IOrderedFilter。
ASP.NET MVC包括一些预定义的操作过滤器,如 请求 HTTPS 的过滤器,授权调用,处理错误或缓存数据。
将在本章后面的“验证和授权”部分中介绍使用特性Authorize。
创建数据驱动的应用程序
现在你已经阅读了ASP.NET MVC的所有基础,是时候来看一个使用ADO.NET实体框架的数据驱动的应用程序。可以看到ASP.NET MVC结合数据访问提供的功能。
注意 ADO.NET实体框架在第38章“实体框架核心”中有详细介绍。
示例应用程序 MenuPlanner 用于维护在数据库中的餐馆菜单条目。只有经过身份验证的帐户才可以执行数据库条目的维护。未经身份验证的用户则可以浏览菜单。
该项目是通过使用 ASP.NET Core 1.0 Web 应用程序模板创建的。身份验证使用默认选择的个人用户帐户。这个项目模板为ASP.NET MVC和控制器添加了几个文件夹,包括HomeController和AccountController。它还添加了一些脚本库。
定义模型
首先在 Models 目录中定义一个模型。使用ADO.NET实体框架创建模型。 MenuCard类型定义了一些属性和与菜单列表的关系(代码文件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; } }
与数据库的连接,以及 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应用程序的启动代码定义了用作数据上下文的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. }
创建数据库
可以使用Entity Framework命令来创建用于创建数据库的代码。命令行提示符中可以使用.NET核心命令行(CLI)和ef命令创建代码以自动创建数据库。要使用命令提示符,必须将当前文件夹设置为project.json文件所在的目录:
>dotnet ef migrations add InitMenuCards --context MenuCardsContext
注意 dotnet工具在第1章“.NET应用程序体系结构”和第17章“Visual Studio 2015”中讨论。
因为多个数据上下文( MenuCardsContext 和 ApplicationDbContext )是通过项目定义的,所以需要使用--context选项指定数据上下文。 ef命令在项目结构创建一个Migrations文件夹, InitMenuCards类中使用Up方法创建数据库表,使用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"); } }
现在只需要一些代码来启动迁移进程,用初始样本数据填充数据库。 MenuCardDatabaseInitializer 通过在从 Database 属性返回的DatabaseFacade对象上调用扩展方法 MigrateAsync 来应用迁移过程。这反过来检查与连接字符串相关联的数据库是否已具有与通过迁移指定的数据库相同的版本。如果它不具有相同的版本,则调用所需的Up方法以获得相同的版本。除此之外,创建几个MenuCard对象将它们存储在数据库中(代码文件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(); } } } }
随着数据库和模型到位,可以创建一个服务。
创建服务
在创建服务之前,创建接口IMenuCardsService,该接口定义服务所需的所有方法(代码文件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); } }
服务类MenuCardsService实现了返回菜单和菜单卡的方法,创建、更新和删除菜单(代码文件 MenuPlanner/Services/MenuCardsService.cs):
作者:沐汐 Vicky
出处:http://www.cnblogs.com/EasyInvoice
欢迎转载,但未经作者同意必须保留此段声明,否则保留追究法律责任的权利.
http://www.cnblogs.com/EasyInvoice/p/6464089.html