精通 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
方法里面注册一下自定义的依懒性解析器:
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
现在,我们接着看看与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
,其中包含了要被显示出来的商品的集合、页面的分页信息跟当前显示的分类信息。下面是这个视图模型的具体内容:
public class ProductsListViewModel
{
public IEnumerable<Product> Products { get; set; }
public PagingInfo PagingInformation { get; set; }
public string CurrentCategory { get; set; }
}
由于这个模型与业务中的域模型并没有什么关系,所以他被定义在 WebUI.cs 中。
NavController
接下来是NavController
,用来给用户提供一个分类显示商品的方法,在页面中的体现就是导航栏。
为了实现这个功能,我们使用了 MVC 框架中的“子动作”,这依赖于一个叫做“RenderAction”的Html辅助器的方法,他可以让用户在当前视图中包含任意一个动作方法的输出(不是以iframe
的形式)。
下面是这个控制器的内容:
public class NavController : Controller
{
private IProductsRepository respository;
public NavController(IProductsRepository repo)
{
respository = repo;
}
public PartialViewResult Menu(string category = null)
{
ViewBag.SelectedCategory = category;
IEnumerable<string> categories = respository.Products
.Select(x => x.Category)
.Distinct()
.OrderBy(x => x);
return PartialView(categories);
}
}
这个控制器的构造函数也是跟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行的代码,这里使用了RenderPartial
HTML辅助器,它会直接将内容写入当前页面,不返回任何值。在这里,我们使用了参数为(视图名,模型)的重载。其中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:用来约束上面查询字符串的正则表达式
具体是如何实现的,等我去研究一下。。。
精通 ASP.NET MVC 4 学习笔记(一)的更多相关文章
- ASP.NET MVC Routing学习笔记(一)
Routing在ASP.NET MVC中是非常核心的技术,属于ASP.NET MVC几大核心技术之一,在使用Routing之前,得先引入System.Web.Routing,但其实不用这么麻烦,因为在 ...
- ASP.NET MVC过滤器学习笔记
1.过滤器的两个特征 1.他是一种特性,可以引用到控制器类和Action方法上.比如下图 这里控制器类和action方法都引用了过滤器,这个过滤器是用来做授权的 2.特征继承自FilterAttrib ...
- 精通ASP.Net MVC 3 框架(第三版)学习笔记
精通ASP.Net MVC 3 框架(第三版)学习笔记 代码才是王道. http://pan.baidu.com/s/1pJyL1cn
- sql server 关于表中只增标识问题 C# 实现自动化打开和关闭可执行文件(或 关闭停止与系统交互的可执行文件) ajaxfileupload插件上传图片功能,用MVC和aspx做后台各写了一个案例 将小写阿拉伯数字转换成大写的汉字, C# WinForm 中英文实现, 国际化实现的简单方法 ASP.NET Core 2 学习笔记(六)ASP.NET Core 2 学习笔记(三)
sql server 关于表中只增标识问题 由于我们系统时间用的过长,数据量大,设计是采用自增ID 我们插入数据的时候把ID也写进去,我们可以采用 关闭和开启自增标识 没有关闭的时候 ,提示一下错 ...
- ASP.NET Core MVC 网站学习笔记
ASP.NET Core MVC 网站学习笔记 魏刘宏 2020 年 2 月 17 日 最近因为” 新冠” 疫情在家办公,学习了 ASP.NET Core MVC 网站的一些知识,记录如下. 一.新建 ...
- Asp.Net Core WebApi学习笔记(四)-- Middleware
Asp.Net Core WebApi学习笔记(四)-- Middleware 本文记录了Asp.Net管道模型和Asp.Net Core的Middleware模型的对比,并在上一篇的基础上增加Mid ...
- ASP.NET Core 2 学习笔记(七)路由
ASP.NET Core通过路由(Routing)设定,将定义的URL规则找到相对应行为:当使用者Request的URL满足特定规则条件时,则自动对应到相符合的行为处理.从ASP.NET就已经存在的架 ...
- ASP.NET Core 2 学习笔记(十二)REST-Like API
Restful几乎已算是API设计的标准,通过HTTP Method区分新增(Create).查询(Read).修改(Update)和删除(Delete),简称CRUD四种数据存取方式,简约又直接的风 ...
- ASP.NET Core 2 学习笔记(十)视图
ASP.NET Core MVC中的Views是负责网页显示,将数据一并渲染至UI包含HTML.CSS等.并能痛过Razor语法在*.cshtml中写渲染画面的程序逻辑.本篇将介绍ASP.NET Co ...
随机推荐
- Leetcode: Guess Number Higher or Lower II
e are playing the Guess Game. The game is as follows: I pick a number from 1 to n. You have to guess ...
- G面经prepare: Set Intersection && Set Difference
求两个sorted数组的intersection e.g. [1,2,3,4,5],[2,4,6] 结果是[2,4] difference 类似merge, 分小于等于大于三种情况,然后时间O(m+n ...
- java 获取当前系统系时间
//SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式 SimpleDateFo ...
- webDriver中的alert
driver.switchTo().alert();这句可以得到alert\confirm\prompt对话框的对象,然后运用其方法对它进行操作.对话框操作的主要方法有: getText() ...
- 对while((pid = waitpid(-1, &stat, WNOHANG)) > 0)不懂的地方,现在懂了
while((pid = waitpid(-1, &stat, WNOHANG)) > 0) 需要写到信号处理函数中,假如有10个子进程 只要父进程能够收到最后一个信号,就能把前面丢失的 ...
- 封装mysql类
类: <?phpheader("content-type:text/html;charset=utf-8");//封装一个类/*掌握满足单例模式的必要条件(1)私有的构造方法 ...
- HDU 4718 The LCIS on the Tree(树链剖分)
Problem Description For a sequence S1, S2, ... , SN, and a pair of integers (i, j), if 1 <= i < ...
- App架构设计学习(一)---- 接口的设计
一.哎,最近换了家工作,结果工作很出的我意外,没有干熟悉的根据需求写代码,反而让我一个小菜鸟去重构一下App的架构(他们公司的app,已经上线了1.0版本了),没办法,只有硬着头皮去先学习学习,再总结 ...
- Android Handler简单使用
package com.example.myhandlertest3; import android.os.Bundle; import android.os.Handler; import andr ...
- x3270: PCOM的替代品
在Linux上登录Mainframe的利器,而且也是PCOM的免费版.原来没有仔细研究,今天花了一些时间学习,确实能满足基本的要求: x3270的Homepage 看一下Release Note, ...