問題 我們想快速啟動一個 ASP.NET Web API 解決方案。 解決方案 APS.NET 模板一開始就支持 ASP.NET Web API。使用模板往我們的項目中添加 Controller,在我們解決方案的 Controllers 文件夾上右鍵,選擇“添加”->"Scaffolding"。 即 ...
問題
我們想快速啟動一個 ASP.NET Web API 解決方案。
解決方案
APS.NET 模板一開始就支持 ASP.NET Web API。使用模板往我們的項目中添加 Controller,在我們解決方案的 Controllers 文件夾上右鍵,選擇“添加”->"Scaffolding"。
即用模式,可以從下麵選擇一個:
-
Web API2 Controller
-
Web API2 Controller with actions, using Entity Framework
-
Web API2 Controller with read/write actions
-
Web API2 OData Controller with action, using Entity Framwork
另外,帶有屬性路由的基架模板可以從 NuGet 中下載。Install-Package Microsoft.AspNet.WebApi.ScaffolderTemplates.AttributeRouting.CSharp
工作原理
模板功能的全名是 ASP.NET 模板(Scaffolding),他是一個基於 T4 模板的 ASP.NET 代碼生成框架。T4(Text Template Transformation Toolkit),是一個代碼生成器模板,從 Visual Studio 2005 開始 T4 模板就已經Visual Studio 的一部分了。
Visual Studio 2013 開始對模板的支持更加出色,允許我們快速生成 ASP.NET 應用程式代碼。在 Visual Studio 2013 更新 2 上,一些更具擴展性的功能點被添加進來,比如,模板的可定製化,這就讓我們使用他生成代碼的時候,更加靈活。
內建模板是被安裝在 Visual Studio 安裝文件夾中,我們可以在這裡定製模板。例如,預設安裝的情況下,模板是在
C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\Extensions\Microsoft\Web\Mvc\Scaffolding\Templates 中。需要註意的是,修改任何模板之後,修改變更帶來的影響就是全局的。如果想在每個項目的基礎上自定義模板,可以通過下麵的兩種方式:
-
安裝 SideWaffle(sidewaffle.com),他是 Visual Studion 模板管理的一個擴展程式。然後,使用常規的“添加”對話框,然後選擇“Web”->“SideWaffle”->“ASP.NET Scaffolding T4”。將會在解決方案的文件夾中創建一個 “CodeTempplates” 文件夾,包括所有全局模板的副本,就可以根據我們的項目需要來修改他。
-
手動的將所有全局模板複製到 ASP.NET 項目中“CodeTemplates”(名字很重要)文件夾中,該文件夾是在項目的根目錄中的一個文件夾。這些模板的副本中包含 C# 和 VB.NET 模板,但是,我們可以根據需要進行刪減。要確保文件這些文件已經包含到項目中。
代碼
讓我們演示一個 Web API Controller Code-First 使用基礎模板處理的例子。
模型展示如列表 1-13。
列表 1-13. EF Code-First
1 2 3 4 5 6 |
public class Team{
public int Id { get ; set ; }
public string Name { get ; set ; }
public DateTime FoundingDate { get ; set ; }
public string LeagueName { get ; set ; }
}
|
添加完模型之後,我們在處理模板對話框的時候,需要重新編譯項目。EF 是依賴於我們項目應用程式 DLL的反射。然後,選擇“添加”->“Scaffolding”->“Web API”->“Web API 2 Controller with actions,using Entity Framework”。對話框如圖1-3。
圖 1-3. 添加模板模板對話框。
可以按照圖 1-4 的對話框來處理。必須許選擇一個模型,他是通過全名來限定的(這有一個可用的下拉框,會展示這個項所有的類),Entity Framework DataContext(如果有的話,會在下拉框中展示,也可以直接在這裡創建) 也是通過全名來限定,並且預設的控制器名稱也是和模型名稱一樣。我們可以檢查 “Use async controller actions”選擇框來強制模板引擎生成非同步 action 和使用 EF DataContext 的非同步方法。
生成的 Controller 如清單 1-14(為了節省空間,沒並沒有貼出命名空間)。這是一個完全可以訪問的HTTP url,請求會被預設路由識別匹配。這個創建的 Action(POST)將會響應 201 狀態碼給調用端,並包含一個指向最新創建資源定位的頭。這個更新的 Action(PUT)甚至可能處理一個潛在的異常DbUpdateConcurrencyException.
清單 1-14. 通過模板生成使用 EF Action 的一個 Web API Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
public class TeamsController : ApiController{
private ApressRecipesWebApiContext db = new ApressRecipesWebApiContext();
// GET: api/Teams public IQueryable<Team> GetTeams()
{
return db.Teams;
}
// GET: api/Teams/5 [ResponseType(typeof(Team))]
public async Task<IHttpActionResult> GetTeam( int id)
{
Team team = await db.Teams.FindAsync(id);
if (team == null )
{
return NotFound();
}
return Ok(team);
}
// PUT: api/Teams/5 [ResponseType(typeof(void))]
public async Task<IHttpActionResult> PutTeam( int id, Team team)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != team.Id)
{
return BadRequest();
}
db.Entry(team).State = EntityState.Modified;
try {
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TeamExists(id))
{
return NotFound();
}
else {
throw ;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
// POST: api/Teams [ResponseType(typeof(Team))]
public async Task<IHttpActionResult> PostTeam(Team team)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Teams.Add(team);
await db.SaveChangesAsync();
return CreatedAtRoute( "DefaultApi" , new { id = team.Id }, team);
}
// DELETE: api/Teams/5 [ResponseType(typeof(Team))]
public async Task<IHttpActionResult> DeleteTeam( int id)
{
Team team = await db.Teams.FindAsync(id);
if (team == null )
{
return NotFound();
}
db.Teams.Remove(team);
await db.SaveChangesAsync();
return Ok(team);
}
protected override void Dispose( bool disposing)
{
if (disposing)
{
db.Dispose();
}
base .Dispose(disposing);
}
private bool TeamExists( int id)
{
return db.Teams.Count(e => e.Id == id) > 0;
}
}
|
現在,假設我們已經按照“工作原理”部分描述的方式,在我們的解決方案中添加了基架模板。但是,我們還是希望可以按照自己的方式定義它。例如,強制所有新建的 ASP.NET Web Api 控制器類繼承一個指定的基類,如清單 1-15 所示。我們需要修改 CodeTemplates/ApiControllerEmpty 文件夾中的 Controller.cs.t4文件,以確保每一個新的 Controller 不再繼承自 ApiController,而是成為 ApiBaseController 的子類,這是一個在大項目中典型的需求,因為很多 Web API 開發者喜歡採用自己的基類作為新的 Controller 的基類。
清單1-15. 通過模板強制新建的 Web API Controller 總是繼承自 ApiBaseController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<#@ template language= "C#" HostSpecific= "True" #>
<#@ output extension= "cs" #>
<#@ parameter type= "System.String" name= "ControllerName" #>
<#@ parameter type= "System.String" name= "Namespace" #>
using System; using System.Collections.Generic;
using System.Linq; using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace <#= Namespace #>
{
public class <#= ControllerName #> : ApiBaseController
{
}
}
|
如果現在來“添加”->“Scaffolding”->“Web API”->“Web API2 Controller Empty”,生成的代碼如清單1-16 所示,繼承自 ApiBaseController 而不是 ApiCnotroller。
清單1-16. 根據自定義模板生成的 Controller
Listing 1-16. A Controller Generated from the Customized Scaffolding Template
1 2 3 4 5 |
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; namespace Apress.Recipes.WebApi.Controllers
{
public class SampleController : ApiBaseController {
}
}
|
我們可以在更廣泛的範圍去使用這個定製化的技術,自定義命名空間,註入自己的服務,或者強制 action是非同步的。
小提示 不僅僅修改現有的,也可以添加新的,完全獨立的模板。我們可以在學習更多官方的 .NET Web Development 和 Tools 小組的小組的博客,請戳這裡
https://blogs.msdn.microsoft.com/webdev/2014/04/03/creating-a-custom-scaffolder-for-visual-studio/