精通 ASP.NET MVC 4 学习笔记(一)
這里記錄著從 P132 到 P192 的內容。水分很足,大部分是書上的代碼,我只加了一些基于我自己的理解的能幫助初學者看懂的注釋,并且把書中的部分內容做了一些的拓展。
建立數據層
設置 DI 容器
/// <summary>/// 設置 DI 容器/// </summary>/// <seealso cref="System.Web.Mvc.DefaultControllerFactory" />public class NinjectControllerFactory : DefaultControllerFactory{private IKernel ninjectKernel;//添加依賴綁定規則private void AddBindings(){//put bindings here//將所有對 IProductsRepository 接口的實現替換為對 EFProductRepository 類的實現!!!ninjectKernel.Bind<IProductsRepository>().To<EFProductRepository>();}//必要的public NinjectControllerFactory(){ninjectKernel = new StandardKernel();AddBindings();}protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType){return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType);}}這個NinjectControllerFactory是一個繼承于DefaultControllerFactory的自定義的依賴性解釋器,這樣,我們的就可以手動的控制查找控制器的方式。這里,我們把所有對 IProductsRepository 接口的實現替換為對 EFProductRepository 類的實現。
然后,在我們的應用中的 Global.asax.cs 文件中的Application_Start方法里面注冊一下自定義的依懶性解析器:
現在,我們接著看看與IProductsRepository相關的東西,首先是這個這個接口的內容:
/// <summary> /// 通過存儲庫模式實現持久化邏輯與域模型實體的分離。 /// </summary> public interface IProductsRepository {IQueryable<Product> Products { get; } }Products 屬性存著來自數據庫的 Product 表內容。
使用 EF 連接到數據庫
然后我們再看看EFProductRepository的內容。
/// <summary> /// 通過實現 IProductsRepository 接口實現的存儲庫類。 /// </summary> /// <seealso cref="SportsStore.Domain.Abstract.IProductsRepository" /> public class EFProductRepository : IProductsRepository {/// <summary>/// 用來定義從實體對象到數據庫的映射。/// </summary>private EFDbContext context = new EFDbContext();public IQueryable<Product> Products{get { return context.Products; }} }這就是存儲庫類,他實現了IProductsRepository接口,而且用了一個EFDbContext實例,便于 Entity Framework 接收數據庫的數據,然后通過Products屬性以只讀的方式訪問數據庫實體。
然后讓我們看看EFDbContext的內容:
public class EFDbContext : DbContext {/// <summary>/// 表示用 Product 模型類型來表示 Products 表中的每一行。/// </summary>/// <value>/// Products 表中所有行的集合。/// </value>public DbSet<Product> Products { get; set; } }它繼承于DbContext,定義了從實體對象到數據庫的映射。除此之外,他還提供了如下的服務:
- 跟蹤已經檢索到的實體對象,如果再次查詢該對象,就直接從對象上下文中提取它。
- 保存實體的狀態信息。可以獲得已添加、修改和刪除對象的信息。
- 更新對象上下文中的實體,把改變的內容寫入底層的存儲器中。
然后他還有一個DataSet屬性,DataSet表示上下文中給定類型的所有實體的集合或可從數據庫中查詢的給定類型的所有實體的集合,基本上就是一個存在于內存的數據庫,其中包含了所有表、關系和約束。
僅僅是這么做是不夠的,我們還需要告訴 Entity Framework 如何連接到數據庫,我們要在 Web.config 文件中添加一條連接字符串,就像在 WebForms 網站中做的一樣:
<connectionStrings><add name="EFDbContext" connectionString="Data Source=DESKTOP-M31J37E;Initial Catalog=SportsStore;Integrated Security=True" providerName="System.Data.SqlClient" /></connectionStrings>添加控制器
ProductController
首先是書中最先添加的 ProductController:
public class ProductController : Controller{private IProductsRepository repository;public int PageSize = 4;public ProductController(IProductsRepository productReposotory){this.repository = productReposotory;}/// <summary>/// 顯示指定目錄、指定頁面的商品。/// </summary>/// <param name="category">The category.</param>/// <param name="page">The page.</param>/// <returns>返回一個 ProductsListViewModel 模型的視圖</returns>public ViewResult List(string category, int page = 1){ProductsListViewModel model = new ProductsListViewModel{Products = repository.Products.Where(p => category == null || p.Category == category).OrderBy(p => p.ProductID).Skip((page - 1) * PageSize).Take(PageSize),PagingInformation = new PagingInfo{CurrentPage = page,ItemsPerPage = PageSize,TotalItems = category == null ? repository.Products.Count() :repository.Products.Where(e => e.Category == category).Count()},CurrentCategory = category};return View(model);}}這里需要關注的是這個控制器的構造函數,他需要一個商品存儲庫類型的參數,這個參數將由我們之前設置的 DI 容器自動注入,也就是一個被映射有數據庫內容的EFProductRepository對象,這樣,我們的控制器就可以獲取到數據庫的內容了。
然后List方法中,創建了一個ProductsListViewModel,其中包含了要被顯示出來的商品的集合、頁面的分頁信息跟當前顯示的分類信息。下面是這個視圖模型的具體內容:
由于這個模型與業務中的域模型并沒有什么關系,所以他被定義在 WebUI.cs 中。
NavController
接下來是NavController,用來給用戶提供一個分類顯示商品的方法,在頁面中的體現就是導航欄。
為了實現這個功能,我們使用了 MVC 框架中的“子動作”,這依賴于一個叫做“RenderAction”的Html輔助器的方法,他可以讓用戶在當前視圖中包含任意一個動作方法的輸出(不是以iframe的形式)。
下面是這個控制器的內容:
這個控制器的構造函數也是跟ProductController.List的構造函數一樣,由 DI 來為他們注入參數。
Menu方法返回一個分部視圖,這樣可以讓視圖片段跨視圖重用,有助于減少重復,分部視圖在渲染的時候只是生成 HTML 片段。
為了能夠完整的把 MVC 的思想體現出來,接下來將會介紹與上面兩個控制器相關的視圖。
視圖
List
@model SportsStore.WebUI.Models.ProductsListViewModel@{ViewBag.Title = "Products"; }@foreach (var p in Model.Products) {Html.RenderPartial("ProductSummary", p); }<div class="pager">@Html.PageLinks(Model.PagingInformation, x => Url.Action("List", new { page = x, category = Model.CurrentCategory })) </div>這是一個強類型的視圖,與List方法使用一致的模型。
首先我們需要關注的是第9行的代碼,這里使用了RenderPartialHTML輔助器,它會直接將內容寫入當前頁面,不返回任何值。在這里,我們使用了參數為(視圖名,模型)的重載。其中ProductSummary是一個分部視圖,其中的內容暫且按下不表。
另一個需要關注的地方是第13行我們自定義的Html輔助器,他的內容如下:
public static class PagingHelpers{/// <summary>/// Build "a" tag to navigate from page to others./// </summary>/// <param name="html">The HTML Helper.</param>/// <param name="pageInfo">The page information.</param>/// <param name="pageUrl">The delegate that can create page URL for paging.</param>/// <returns>The Html string.</returns>public static MvcHtmlString PageLinks(this HtmlHelper html, PagingInfo pageInfo, Func<int, string> pageUrl){StringBuilder result = new StringBuilder();for (int i = 1; i <= pageInfo.TotalPages; i++){TagBuilder tag = new TagBuilder("a");tag.MergeAttribute("href", pageUrl(i));tag.InnerHtml = i.ToString();if (i == pageInfo.CurrentPage){tag.AddCssClass("selected");}result.Append(tag.ToString());}return MvcHtmlString.Create(result.ToString());}}PageLinks是一個拓展方法,他接受一個頁面信息參數跟一個用來生成 Url 的函數參數用來生成 HTML 代碼。這個函數參數調用的是Url.Action方法,他可以根據動作方法與數據模型生成指定的 Url 。
Menu
這是與NavController.Menu方法相關的視圖,他的內容如下:
@model IEnumerable<string>@Html.ActionLink("Home", "List", "Product")@foreach (var link in Model) {@Html.RouteLink(link, new {controller = "Product",action = "List",category = link,page = 1 },new { @class = link == ViewBag.SelectedCategory ? "selected" : null } ) }這也是一個強類型視圖,不過在創建這個視圖的時候,VS2015并不能讓我直接從彈出的對話框里面指定我想要的模型,所以我是先創建空模型的視圖,然后手動在源碼里面添加模型的。這個模型里面包含著所有分類的名稱,然后使用RouteLink方法生成對應的a標簽,其中,第一個參數是a標簽的內容,第二個參數是包含有路由信息的屬性,通過檢查對象的屬性,利用反射檢索參數。該對象通常是使用對象初始值設定項語法創建的。第三個參數是一個包含要添加給標簽屬性的對象,在這里,我們使用@class來給標簽元素添加一個 class 。
路由
至此,我們的網站已經有了最基本的功能,但是,頁面的 url 卻很不美觀,這里,我們就需要修改 RouteConfig.cs 里面的內容來產生美觀的 url 。修改后的內容如下:
public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute(name: null,url: "",defaults: new { controller = "Product", action = "List", category = (string)null, page = 1 });routes.MapRoute(name: null,url: "Page{page}",defaults: new { Controller = "Product", action = "List", category = (string)null },constraints: new { page = @"\d+" });routes.MapRoute(name: null,url: "{category}",defaults: new { Controller = "Product", action = "List", page = 1 });routes.MapRoute(name: null,url: "{category}/Page{page}",defaults: new { Controller = "Product", action = "List" },constraints: new { page = @"\d+" }//約束);routes.MapRoute(null, "{controller}/{action}");}}這里面,主要需要注意的是 MapRoute 方法的調用。路由的是按照他定義的順序來運用的,如果改變這種順序會出現奇怪的效果。
現在我來解釋一下這個方法的幾個參數的含義:
- name:表示路由的名稱,目前我感覺木啥卵用
- url:表示路由的格式,大括號內的內容是用來匹配實際 url 的
- defaults:一個包含了控制器,動作方法的對象,動作方法參數的對象
- constrains:用來約束上面查詢字符串的正則表達式
具體是如何實現的,等我去研究一下。。。
轉載于:https://www.cnblogs.com/JacZhu/p/5324544.html
總結
以上是生活随笔為你收集整理的精通 ASP.NET MVC 4 学习笔记(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 五款常用邮件管理系统评测
- 下一篇: [译] ASP.NET 生命周期 – A