SportsStorePeta应用程序开发
步骤:
1.创建解决方案和项目(Domain、WebUI、UnitTests)
2.添加引用
3.设置ID容器
4.设计域模型
5.创建抽象存储库
6.创建模仿存储库
7.添加控制器、动作及视图
8.设置路由
9.准备数据库及数据表
10.创建实例框架上下文
11.创建产品存储库
12.添加分页
13.改进路由及URL
14.设置内容样式CSS
15.创建分部视图
一、解决方案:SportsStorePeta,该应用程序将不使用EF,而使用PetaPoco实现
项目:
二、项目引用
项目及依赖性
项目名 | VS模板 | 目的 | 工具依赖 | 项目依赖 | 微软引用 |
SportsStorePeta.Domain | 类库 | 存放域实体与逻辑,EF | None | None |
System.Web.Mvc System.ComponentModel.DataAnnotations |
SportsStorePeta.WebUI | Asp.net MVC4 应用程序 Basic(基本) | 存放控制器与视图 |
Ninject Moq |
SportsStorePeta.Domain | None |
SportsStorePeta.UnitTests | 测试项目 | 单元测试 |
Ninject Moq |
SportsStorePeta.Domain SportsStorePeta.WebUI |
System.Web.Mvc System.Web Microsoft.CSharp |
三、设置ID容器
1.Ninject控制器工厂:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using Ninject; namespace SportsStorePeta.WebUI.Infrastructure { /// <summary> /// 使用Ninject修改默认控制器工厂 /// </summary> public class NinjectControllerFactory :DefaultControllerFactory { private readonly IKernel _ninjectKernel; public NinjectControllerFactory() { _ninjectKernel = new StandardKernel(); AddBindings(); } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { return controllerType == null ? null : (IController) _ninjectKernel.Get(controllerType); } private void AddBindings() { //Ninject绑定 } } }
MVC框架注册NinjectControllerFactory
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using SportsStorePeta.WebUI.Infrastructure; namespace SportsStorePeta.WebUI { // 注意: 有关启用 IIS6 或 IIS7 经典模式的说明, // 请访问 http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); //MVC框架注册NinjectControllerFactory ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory()); } } }
四、设计域模型 和视图模型
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SportsStorePeta.Domain.Entities { public class Product { public int ProductId { get; set; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public string Category { get; set; } } }
视图模型
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace SportsStorePeta.WebUI.Models { public class ProductViewModel { public int ProductId { get; set; } public string Name { get; set; } public string Description { get; set; } public string Price { get; set; } public string Category { get; set; } } }
五、创建抽象存储库
using System; using System.Collections.Generic; using System.Linq; using System.Text; using SportsStorePeta.Domain.Entities; namespace SportsStorePeta.Domain.Abstract { public interface IProductRepository { IQueryable<Product> Products { get; } } }
六、创建模仿存储库
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using Moq; using Ninject; using SportsStorePeta.Domain.Abstract; using SportsStorePeta.Domain.Entities; namespace SportsStorePeta.WebUI.Infrastructure { /// <summary> /// 使用Ninject修改默认控制器工厂 /// </summary> public class NinjectControllerFactory :DefaultControllerFactory { private readonly IKernel _ninjectKernel; public NinjectControllerFactory() { _ninjectKernel = new StandardKernel(); AddBindings(); } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { return controllerType == null ? null : (IController) _ninjectKernel.Get(controllerType); } private void AddBindings() { //Ninject绑定 //1.添加模拟IproductRepository实现 Mock<IProductRepository> mock=new Mock<IProductRepository>(); mock.Setup(m => m.Products).Returns( new List<Product> { new Product {Name = "Football", Price = 35}, new Product {Name = "Surf board", Price = 179}, new Product {Name = "Running shoes", Price = 87} }.AsQueryable()); //永久绑定 _ninjectKernel.Bind<IProductRepository>().ToConstant(mock.Object); } } }
七、添加控制器、动作及视图
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SportsStorePeta.Domain.Abstract; using SportsStorePeta.Domain.Entities; using SportsStorePeta.WebUI.Models; namespace SportsStorePeta.WebUI.Controllers { public class ProductController : Controller { private readonly IProductRepository _repository; public ProductController(IProductRepository productRepository) { _repository = productRepository; } public ViewResult List() { List<ProductViewModel> productViewModels = new List<ProductViewModel>(); foreach (Product product in _repository.Products) { ProductViewModel productViewModel = new ProductViewModel { ProductId = product.ProductId, Name = product.Name, Description = product.Description, Price = product.Price, Category = product.Category }; productViewModels.Add(productViewModel); } return View(productViewModels); } } }
视图
@model IEnumerable<SportsStorePeta.WebUI.Models.ProductViewModel> @{ ViewBag.Title = "商品"; } @foreach (var p in Model) { <div class="item"> <h3>@p.Name</h3> @p.Description <h4>@p.Price</h4> </div> }
八、设置路由
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace SportsStorePeta.WebUI { public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Product", action = "List", id = UrlParameter.Optional } ); } } }
九、准备数据库及数据表
建表SQL语句
Create table Products( [ProductId] INT NOT NULL PRIMARY KEY IDENTITY, [Name] NVARCHAR(100) NOT NULL, [Description] NVARCHAR(500) NOT NULL, [Category] NVARCHAR(50) NOT NULL, [Price] DECIMAL(16,2) NOT NULL )
表建好后,可以插入一些数据做显示用。
INSERT INTO [Products] ([Name],[Description],[Category],[Price]) VALUES (N‘Lifejacket‘,N‘Protective and fashionbale‘,N‘Watersports‘,48.95); INSERT INTO [Products] ([Name],[Description],[Category],[Price]) VALUES (N‘Soccer Ball‘,N‘FIFA-approved size and weight‘,N‘Soccer‘,19.50); INSERT INTO [Products] ([Name],[Description],[Category],[Price]) VALUES (N‘CornerFlags‘,N‘Give your playing field a protessional ‘,N‘Soccer‘,34.50); INSERT INTO [Products] ([Name],[Description],[Category],[Price]) VALUES (N‘蓝球‘,N‘CornerFlagssafasdfsdf‘,N‘Soccer‘,34.20); INSERT INTO [Products] ([Name],[Description],[Category],[Price]) VALUES (N‘CornerFlags12‘,N‘CornerFlagssafasdfsdf‘,N‘Soccer‘,43.20); INSERT INTO [Products] ([Name],[Description],[Category],[Price]) VALUES (N‘CornerFlags13‘,N‘CornerFlagssafasdfsdf‘,N‘Soccer‘,11.23); INSERT INTO [Products] ([Name],[Description],[Category],[Price]) VALUES (N‘CornerFlags14‘,N‘CornerFlagssafasdfsdf‘,N‘Soccer‘,123.23); INSERT INTO [Products] ([Name],[Description],[Category],[Price]) VALUES (N‘CornerFlagssafasdfsdf‘,N‘CornerFlagssafasdfsdf‘,N‘Soccer‘,32.23);
十、创建实例框架上下文
1.NuGet中引入PetaPoco
2.web.config添加数据库连接
<connectionStrings> <add name="DbContext" providerName="System.Data.SqlClient" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=SportsStore;Integrated Security=True" /> </connectionStrings>
3.设置PetaPoco,修改DataBase.tt模板
ConnectionStringName = "DbContext"; Namespace = "SportsStorePeta.Domain.Entities";
修改PetaPoco.Core.ttinclude
<#@ template language="C#" hostspecific="True" #>
4.新建上下文类:PpDbContext
using System; using System.Collections.Generic; using System.Linq; using System.Text; using SportsStorePeta.Domain.Entities; namespace SportsStorePeta.Domain.Concrete { public class PpContext : DbContextDB { public IQueryable<Product> Products { get { var products = base.Query<Product>("Select ProductId,Name,Description,Category,Price from Products").AsQueryable(); return products; } } } }
十一、创建产品存储库PpProductRepository
using System; using System.Collections.Generic; using System.Linq; using System.Text; using SportsStorePeta.Domain.Abstract; using SportsStorePeta.Domain.Entities; namespace SportsStorePeta.Domain.Concrete { public class PpProductRepository :IProductRepository { private readonly PpContext _context=new PpContext(); public IQueryable<Product> Products { get { return _context.Products; } } } }
修改Ninject绑定:
private void AddBindings() { _ninjectKernel.Bind<IProductRepository>().To<PpProductRepository>(); }
十二、添加分页
修改ProductController
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SportsStorePeta.Domain.Abstract; using SportsStorePeta.Domain.Entities; using SportsStorePeta.WebUI.Models; namespace SportsStorePeta.WebUI.Controllers { public class ProductController : Controller { private readonly IProductRepository _repository; public int PageSize = 4; public ProductController(IProductRepository productRepository) { _repository = productRepository; } public ViewResult List(int page=1) { List<ProductViewModel> productViewModels = new List<ProductViewModel>(); foreach (Product product in _repository.Products) { ProductViewModel productViewModel = new ProductViewModel { ProductId = product.ProductId, Name = product.Name, Description = product.Description, Price = product.Price, Category = product.Category }; productViewModels.Add(productViewModel); } return View(productViewModels.OrderBy(p => p.ProductId).Skip((page - 1)*PageSize).Take(PageSize)); } } }
1.添加分页视图模型
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace SportsStorePeta.WebUI.Models { public class PageInfo { public int TotalItems { get; set; } public int ItemsPerPage { get; set; } public int CurrentPage { get; set; } public int TotalPages { get { return (int) Math.Ceiling((decimal) TotalItems/ItemsPerPage); } } } }
2.添加HTML辅助器方法
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.Web.Mvc; using SportsStorePeta.WebUI.Models; namespace SportsStorePeta.WebUI.HtmlHelpers { public static class PagingHelpers { public static MvcHtmlString PageLinks(this HtmlHelper html, PageInfo 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()); } } }
3.修改Views/Web.config
<namespaces> <add namespace="System.Web.Mvc" /> <add namespace="System.Web.Mvc.Ajax" /> <add namespace="System.Web.Mvc.Html" /> <add namespace="System.Web.Optimization"/> <add namespace="System.Web.Routing" /> <add namespace="SportsStorePeta.WebUI.HtmlHelpers"/> </namespaces>
4.添加视图模型数据 ProductsListViewModel
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace SportsStorePeta.WebUI.Models { public class ProductsListViewModel { public IEnumerable<ProductViewModel> Products { get; set; } public PageInfo PagingInfo { get; set; } } }
5.更新List动作
添加新方法处理域模型
/// <summary> /// 根据Product域模型集合获得视图模型集合 /// </summary> /// <param name="products"></param> /// <returns></returns> private IEnumerable<ProductViewModel> GetProductViewModelListByProducts(IQueryable<Product> products) { List<ProductViewModel> productsViewModels = new List<ProductViewModel>(); foreach (Product product in products) { ProductViewModel productViewModel = new ProductViewModel() { ProductId = product.ProductId, Name = product.Name, Category = product.Category, Description = product.Description, Price = product.Price.ToString("C") }; productsViewModels.Add(productViewModel); } return productsViewModels; }
public ViewResult List(int page=1) { ProductsListViewModel model = new ProductsListViewModel { Products = GetProductViewModelListByProducts( _repository.Products.OrderBy(p => p.ProductId).Skip((page - 1)*PageSize).Take(PageSize)), PagingInfo = new PageInfo { CurrentPage = page,ItemsPerPage = PageSize,TotalItems = _repository.Products.Count()} }; return View(model); }
6.更新List.cshtml视图
@model SportsStorePeta.WebUI.Models.ProductsListViewModel @{ ViewBag.Title = "商品"; } @foreach (var p in Model.Products) { <div class="item"> <h3>@p.Name</h3> @p.Description <h4>@p.Price</h4> </div> } <div class="pager"> @Html.PageLinks(Model.PagingInfo,x=>Url.Action("List",new{page=x})) </div>
十三、改进路由及URL
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: null, url: "Page{page}", defaults: new {controller = "Product", action = "List"}); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Product", action = "List", id = UrlParameter.Optional } ); }
十四、设置内容样式CSS
1.定义布局模板视图_Layout.cshtml
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> <link href="/Content/Site.css" type="text/css" rel="stylesheet"/> </head> <body> <div id="header"> <div class="title">体育用品</div> </div> <div id="categories"> 商品类别 </div> <div id="content"> @RenderBody() </div> </body> </html>
2.添加CSS样式
在site.css 最下面增加:
BODY { font-family: Cambria, Georgia, "Times New Roman"; margin: 0; } DIV#header DIV.title, DIV.item H3, DIV.item H4, DIV.pager A { font: bold 1em "Arial Narrow", "Franklin Gothic Medium", Arial; } DIV#header { background-color: #444; border-bottom: 2px solid #111; color: White; } DIV#header div.title { font-size: 2em; padding: .6em; } DIV#content { border-left: 2px solid gray; margin-left: 9em; padding: 1em; } DIV#categories { float: left; width: 8em; padding: .3em; } DIV.item { border-top: 1px dotted gray; padding-top: .7em; margin-bottom: .7em; } DIV.item:first-child { border-top:none; padding-top: 0; } DIV.item H3 { font-size: 1.3em; margin: 0 0 .25em 0; } DIV.item H4 { font-size: 1.1em; margin:.4em 0 0 0; } DIV.pager { text-align:right; border-top: 2px solid silver; padding: .5em 0 0 0; margin-top: 1em; } DIV.pager A { font-size: 1.1em; color: #666; text-decoration: none; padding: 0 .4em 0 .4em; } DIV.pager A:hover { background-color: Silver; } DIV.pager A.selected { background-color: #353535; color: White; }
十五、创建分部视图
分部视图是嵌入在另一个视图中的一个内容片段。是自包含文件,可以跨视图重用。
1.在Shared中添加分部视图ProductSummary.cshtml
@model SportsStorePeta.WebUI.Models.ProductViewModel <div class="item"> <h3>@Model.Name</h3> @Model.Description <h4>@Model.Price</h4> </div>
2.修改List.cshtml
@model SportsStorePeta.WebUI.Models.ProductsListViewModel @{ ViewBag.Title = "商品"; } @foreach (var p in Model.Products) { Html.RenderPartial("ProductSummary",p); } <div class="pager"> @Html.PageLinks(Model.PagingInfo,x=>Url.Action("List",new{page=x})) </div>